Remove drone & dead code in pkg/build; update go modules (#109935)
* remove drone & dead code in pkg/build; update go modules * remove .drone.star * Remove drone scripts and drone references in Makefile * make update-workspace * remove deadcode tool * Remove daggerbuild/scripts: deadcode * Remove drone files / folders in CODEOWNERS * make update-workspace * remove more dead code
This commit is contained in:
@@ -1,127 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/grafana/grafana/pkg/build/docker"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
)
|
||||
|
||||
const (
|
||||
alpine = "alpine"
|
||||
ubuntu = "ubuntu"
|
||||
)
|
||||
|
||||
// GetImageFiles returns the list of image (.img, but should be .tar because they are tar archives) files that are
|
||||
// created in the 'tag' process and stored in the prerelease bucket, waiting to be released.
|
||||
func GetImageFiles(grafana string, version string, architectures []config.Architecture) []string {
|
||||
bases := []string{alpine, ubuntu}
|
||||
images := []string{}
|
||||
for _, base := range bases {
|
||||
for _, arch := range architectures {
|
||||
image := fmt.Sprintf("%s-%s-%s.img", grafana, version, arch)
|
||||
if base == "ubuntu" {
|
||||
image = fmt.Sprintf("%s-%s-ubuntu-%s.img", grafana, version, arch)
|
||||
}
|
||||
|
||||
images = append(images, image)
|
||||
}
|
||||
}
|
||||
|
||||
return images
|
||||
}
|
||||
|
||||
func FetchImages(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := docker.Config{
|
||||
Archs: buildConfig.Docker.Architectures,
|
||||
Distribution: buildConfig.Docker.Distribution,
|
||||
Bucket: buildConfig.Docker.PrereleaseBucket,
|
||||
Edition: c.String("edition"),
|
||||
Tag: metadata.GrafanaVersion,
|
||||
}
|
||||
|
||||
grafana := "grafana"
|
||||
if cfg.Edition == "enterprise" {
|
||||
grafana = "grafana-enterprise"
|
||||
}
|
||||
if cfg.Edition == "enterprise2" {
|
||||
grafana = "grafana-enterprise2"
|
||||
}
|
||||
if cfg.Edition == "grafana" || cfg.Edition == "oss" {
|
||||
grafana = "grafana-oss"
|
||||
}
|
||||
|
||||
baseURL := fmt.Sprintf("gs://%s/%s/", cfg.Bucket, cfg.Tag)
|
||||
images := GetImageFiles(grafana, cfg.Tag, cfg.Archs)
|
||||
|
||||
log.Printf("Fetching images [%v]", images)
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := DownloadImages(baseURL, images, "."); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := LoadImages(images, "."); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadImages uses the `docker load -i` command to load the image tar file into the docker daemon so that it can be
|
||||
// tagged and pushed.
|
||||
func LoadImages(images []string, source string) error {
|
||||
p := filepath.Clean(source)
|
||||
for _, image := range images {
|
||||
image := filepath.Join(p, image)
|
||||
log.Println("Loading image", image)
|
||||
//nolint:gosec
|
||||
cmd := exec.Command("docker", "load", "-i", image)
|
||||
cmd.Dir = "."
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("out: %s\n", out)
|
||||
return fmt.Errorf("error loading image: %q", err)
|
||||
}
|
||||
log.Println("Loaded image", image)
|
||||
}
|
||||
log.Println("Images successfully loaded!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func DownloadImages(baseURL string, images []string, destination string) error {
|
||||
for _, image := range images {
|
||||
p := baseURL + image
|
||||
log.Println("Downloading image", p)
|
||||
//nolint:gosec
|
||||
cmd := exec.Command("gsutil", "-m", "cp", "-r", p, destination)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download: %w\n%s", err, out)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetImageFiles(t *testing.T) {
|
||||
var (
|
||||
architectures = []config.Architecture{
|
||||
config.ArchAMD64,
|
||||
config.ArchARM64,
|
||||
config.ArchARMv7,
|
||||
}
|
||||
)
|
||||
|
||||
t.Run("1.2.3", func(t *testing.T) {
|
||||
expect := []string{
|
||||
"grafana-oss-1.2.3-amd64.img",
|
||||
"grafana-oss-1.2.3-arm64.img",
|
||||
"grafana-oss-1.2.3-armv7.img",
|
||||
"grafana-oss-1.2.3-ubuntu-amd64.img",
|
||||
"grafana-oss-1.2.3-ubuntu-arm64.img",
|
||||
"grafana-oss-1.2.3-ubuntu-armv7.img",
|
||||
}
|
||||
|
||||
res := GetImageFiles("grafana-oss", "1.2.3", architectures)
|
||||
|
||||
require.Equal(t, expect, res)
|
||||
})
|
||||
|
||||
t.Run("1.2.3+example-01", func(t *testing.T) {
|
||||
expect := []string{
|
||||
"grafana-oss-1.2.3+example-01-amd64.img",
|
||||
"grafana-oss-1.2.3+example-01-arm64.img",
|
||||
"grafana-oss-1.2.3+example-01-armv7.img",
|
||||
"grafana-oss-1.2.3+example-01-ubuntu-amd64.img",
|
||||
"grafana-oss-1.2.3+example-01-ubuntu-arm64.img",
|
||||
"grafana-oss-1.2.3+example-01-ubuntu-armv7.img",
|
||||
}
|
||||
|
||||
res := GetImageFiles("grafana-oss", "1.2.3+example-01", architectures)
|
||||
|
||||
require.Equal(t, expect, res)
|
||||
})
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var (
|
||||
jobsFlag = cli.IntFlag{
|
||||
Name: "jobs",
|
||||
Usage: "Number of parallel jobs",
|
||||
}
|
||||
buildIDFlag = cli.StringFlag{
|
||||
Name: "build-id",
|
||||
Usage: "Optionally supply a build ID to be part of the version",
|
||||
}
|
||||
editionFlag = cli.StringFlag{
|
||||
Name: "edition",
|
||||
Usage: "The edition of Grafana to build (oss or enterprise)",
|
||||
Value: "oss",
|
||||
}
|
||||
triesFlag = cli.IntFlag{
|
||||
Name: "tries",
|
||||
Usage: "Specify number of tries before failing",
|
||||
Value: 1,
|
||||
}
|
||||
tagFlag = cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Grafana version tag",
|
||||
}
|
||||
securityFlag = cli.BoolFlag{
|
||||
Name: "security",
|
||||
Usage: "Security release",
|
||||
}
|
||||
srcFlag = cli.StringFlag{
|
||||
Name: "src-bucket",
|
||||
Value: "grafana-prerelease",
|
||||
Usage: "Google Cloud Storage bucket",
|
||||
}
|
||||
securityDestBucketFlag = cli.StringFlag{
|
||||
Name: "security-dest-bucket",
|
||||
Usage: "Google Cloud Storage bucket for security packages (or $SECURITY_DEST_BUCKET)",
|
||||
}
|
||||
destFlag = cli.StringFlag{
|
||||
Name: "dest-bucket",
|
||||
Value: "grafana-downloads",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
}
|
||||
)
|
||||
@@ -1,406 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/grafana/grafana/pkg/build/gcom"
|
||||
"github.com/grafana/grafana/pkg/build/packaging"
|
||||
"github.com/grafana/grafana/pkg/build/versions"
|
||||
)
|
||||
|
||||
const grafanaAPI = "https://grafana.com/api"
|
||||
|
||||
var httpClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// GrafanaCom implements the sub-command "grafana-com".
|
||||
func GrafanaCom(c *cli.Context) error {
|
||||
bucketStr := c.String("src-bucket")
|
||||
edition := config.Edition(c.String("edition"))
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("couldn't activate service account, err: %w", err)
|
||||
}
|
||||
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseMode, err := metadata.GetReleaseMode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := metadata.GrafanaVersion
|
||||
semver := versions.ParseSemver(version)
|
||||
if releaseMode.Mode == config.Cronjob {
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(bucketStr)
|
||||
latestMainVersion, err := storage.GetLatestMainBuild(c.Context, bucket, filepath.Join(string(edition), "main"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version = latestMainVersion
|
||||
}
|
||||
|
||||
dryRun := c.Bool("dry-run")
|
||||
simulateRelease := c.Bool("simulate-release")
|
||||
// Test release mode and dryRun imply simulateRelease
|
||||
if releaseMode.IsTest || dryRun {
|
||||
simulateRelease = true
|
||||
}
|
||||
|
||||
grafanaAPIKey := strings.TrimSpace(os.Getenv("GRAFANA_COM_API_KEY"))
|
||||
if grafanaAPIKey == "" {
|
||||
return cli.Exit("the environment variable GRAFANA_COM_API_KEY must be set", 1)
|
||||
}
|
||||
|
||||
pkgjson, err := getPackageJSON()
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
whatsNewURL, releaseNotesURL, err := getReleaseURLs(semver, pkgjson)
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
// TODO: Verify config values
|
||||
cfg := packaging.PublishConfig{
|
||||
Config: config.Config{
|
||||
Version: version,
|
||||
},
|
||||
Edition: edition,
|
||||
ReleaseMode: releaseMode,
|
||||
GrafanaAPIKey: grafanaAPIKey,
|
||||
WhatsNewURL: whatsNewURL,
|
||||
ReleaseNotesURL: releaseNotesURL,
|
||||
DryRun: dryRun,
|
||||
TTL: c.String("ttl"),
|
||||
SimulateRelease: simulateRelease,
|
||||
}
|
||||
|
||||
if err := publishPackages(cfg); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
log.Println("Successfully published packages to grafana.com!")
|
||||
return nil
|
||||
}
|
||||
|
||||
type grafanaConf struct {
|
||||
WhatsNewURL string `json:"whatsNewUrl"`
|
||||
ReleaseNotesURL string `json:"releaseNotesUrl"`
|
||||
}
|
||||
|
||||
type packageConf struct {
|
||||
Grafana grafanaConf `json:"grafana"`
|
||||
}
|
||||
|
||||
func getPackageJSON() (*packageConf, error) {
|
||||
pkgB, err := os.ReadFile("package.json")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read package.json: %w", err)
|
||||
}
|
||||
|
||||
var pconf packageConf
|
||||
if err := json.Unmarshal(pkgB, &pconf); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode package.json: %w", err)
|
||||
}
|
||||
|
||||
return &pconf, nil
|
||||
}
|
||||
|
||||
func getReleaseURLs(semver versions.Semver, pconf *packageConf) (string, string, error) {
|
||||
u := fmt.Sprintf(pconf.Grafana.WhatsNewURL, semver.Major, semver.Minor, semver.Patch)
|
||||
if _, err := url.ParseRequestURI(u); err != nil {
|
||||
return "", "", fmt.Errorf("grafana.whatsNewUrl is invalid in package.json: %q", pconf.Grafana.WhatsNewURL)
|
||||
}
|
||||
if _, err := url.ParseRequestURI(pconf.Grafana.ReleaseNotesURL); err != nil {
|
||||
return "", "", fmt.Errorf("grafana.releaseNotesUrl is invalid in package.json: %q",
|
||||
pconf.Grafana.ReleaseNotesURL)
|
||||
}
|
||||
|
||||
return u, pconf.Grafana.ReleaseNotesURL, nil
|
||||
}
|
||||
|
||||
func Builds(baseURL *url.URL, grafana, version string, packages []packaging.BuildArtifact) ([]GCOMPackage, error) {
|
||||
builds := make([]GCOMPackage, len(packages))
|
||||
for i, v := range packages {
|
||||
var (
|
||||
os = v.Distro
|
||||
arch = v.Arch
|
||||
)
|
||||
|
||||
if v.Distro == "windows" {
|
||||
os = "win"
|
||||
if v.Ext == "msi" {
|
||||
os = "win-installer"
|
||||
}
|
||||
}
|
||||
|
||||
if v.Distro == "rhel" {
|
||||
if arch == "aarch64" {
|
||||
arch = "arm64"
|
||||
}
|
||||
if arch == "x86_64" {
|
||||
arch = "amd64"
|
||||
}
|
||||
}
|
||||
|
||||
if v.Distro == "deb" {
|
||||
if arch == "armhf" {
|
||||
arch = "armv7"
|
||||
if v.RaspberryPi {
|
||||
log.Println(v.Distro, arch, "raspberrypi == true")
|
||||
arch = "armv6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u := gcom.GetURL(baseURL, version, grafana, v.Distro, v.Arch, v.Ext, v.Musl, v.RaspberryPi)
|
||||
builds[i] = GCOMPackage{
|
||||
OS: os,
|
||||
URL: u.String(),
|
||||
Arch: arch,
|
||||
}
|
||||
}
|
||||
|
||||
return builds, nil
|
||||
}
|
||||
|
||||
// publishPackages publishes packages to grafana.com.
|
||||
func publishPackages(cfg packaging.PublishConfig) error {
|
||||
log.Printf("Publishing Grafana packages, version %s, %s edition, %s mode, dryRun: %v, simulating: %v...\n",
|
||||
cfg.Version, cfg.Edition, cfg.ReleaseMode.Mode, cfg.DryRun, cfg.SimulateRelease)
|
||||
|
||||
versionStr := fmt.Sprintf("v%s", cfg.Version)
|
||||
log.Printf("Creating release %s at grafana.com...\n", versionStr)
|
||||
|
||||
var (
|
||||
pth string
|
||||
grafana = "grafana"
|
||||
)
|
||||
|
||||
switch cfg.Edition {
|
||||
case config.EditionOSS:
|
||||
pth = "oss"
|
||||
case config.EditionEnterprise:
|
||||
grafana = "grafana-enterprise"
|
||||
pth = "enterprise"
|
||||
default:
|
||||
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
||||
}
|
||||
|
||||
switch cfg.ReleaseMode.Mode {
|
||||
case config.MainMode, config.DownstreamMode, config.CronjobMode:
|
||||
pth = path.Join(pth, packaging.MainFolder)
|
||||
default:
|
||||
pth = path.Join(pth, packaging.ReleaseFolder)
|
||||
}
|
||||
|
||||
pth = path.Join(pth)
|
||||
baseArchiveURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "dl.grafana.com",
|
||||
Path: pth,
|
||||
}
|
||||
|
||||
builds, err := Builds(baseArchiveURL, grafana, cfg.Version, packaging.ArtifactConfigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := Release{
|
||||
Version: cfg.Version,
|
||||
ReleaseDate: time.Now().UTC(),
|
||||
Builds: builds,
|
||||
Stable: cfg.ReleaseMode.Mode == config.TagMode && !cfg.ReleaseMode.IsPreview && !cfg.ReleaseMode.IsTest,
|
||||
Beta: cfg.ReleaseMode.IsPreview,
|
||||
Nightly: cfg.ReleaseMode.Mode == config.CronjobMode,
|
||||
}
|
||||
if cfg.ReleaseMode.Mode == config.TagMode || r.Beta {
|
||||
r.WhatsNewURL = cfg.WhatsNewURL
|
||||
r.ReleaseNotesURL = cfg.ReleaseNotesURL
|
||||
}
|
||||
|
||||
if err := postRequest(cfg, "versions", r, fmt.Sprintf("create release %s", r.Version)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := postRequest(cfg, fmt.Sprintf("versions/%s", cfg.Version), r,
|
||||
fmt.Sprintf("update release %s", cfg.Version)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, v := range r.Builds {
|
||||
sha, err := getSHA256(v.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Builds[i].SHA256 = string(sha)
|
||||
}
|
||||
|
||||
for _, b := range r.Builds {
|
||||
if err := postRequest(cfg, fmt.Sprintf("versions/%s/packages", cfg.Version), b,
|
||||
fmt.Sprintf("create build %s %s", b.OS, b.Arch)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := postRequest(cfg, fmt.Sprintf("versions/%s/packages/%s/%s", cfg.Version, b.Arch, b.OS), b,
|
||||
fmt.Sprintf("update build %s %s", b.OS, b.Arch)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSHA256(u string) ([]byte, error) {
|
||||
shaURL := fmt.Sprintf("%s.sha256", u)
|
||||
|
||||
// nolint:gosec
|
||||
resp, err := http.Get(shaURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Println("failed to close response body, err: %w", err)
|
||||
}
|
||||
}()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("failed downloading %s: %s", u, resp.Status)
|
||||
}
|
||||
|
||||
sha256, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sha256, nil
|
||||
}
|
||||
|
||||
func postRequest(cfg packaging.PublishConfig, pth string, body any, descr string) error {
|
||||
var sfx string
|
||||
switch cfg.Edition {
|
||||
case config.EditionOSS:
|
||||
case config.EditionEnterprise:
|
||||
sfx = packaging.EnterpriseSfx
|
||||
default:
|
||||
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
||||
}
|
||||
product := fmt.Sprintf("grafana%s", sfx)
|
||||
|
||||
jsonB, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to JSON encode release: %w", err)
|
||||
}
|
||||
|
||||
u, err := constructURL(product, pth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u, bytes.NewReader(jsonB))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", cfg.GrafanaAPIKey))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
log.Printf("Posting to grafana.com API, %s - JSON: %s\n", u, string(jsonB))
|
||||
if cfg.SimulateRelease {
|
||||
log.Println("Only simulating request")
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed posting to %s (%s): %s", u, descr, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Println("failed to close response body, err: %w", err)
|
||||
}
|
||||
}()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
|
||||
log.Printf("Already exists: %s\n", descr)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed posting to %s (%s): %s", u, descr, resp.Status)
|
||||
}
|
||||
|
||||
log.Printf("Successfully posted to grafana.com API, %s\n", u)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func constructURL(product string, pth string) (string, error) {
|
||||
productPath := filepath.Clean(filepath.Join("/", product, pth))
|
||||
u, err := url.Parse(grafanaAPI)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
u.Path = path.Join(u.Path, productPath)
|
||||
return u.String(), err
|
||||
}
|
||||
|
||||
type GCOMPackage struct {
|
||||
OS string `json:"os"`
|
||||
URL string `json:"url"`
|
||||
SHA256 string `json:"sha256"`
|
||||
Arch string `json:"arch"`
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
Version string `json:"version"`
|
||||
ReleaseDate time.Time `json:"releaseDate"`
|
||||
Stable bool `json:"stable"`
|
||||
Beta bool `json:"beta"`
|
||||
Nightly bool `json:"nightly"`
|
||||
WhatsNewURL string `json:"whatsNewUrl"`
|
||||
ReleaseNotesURL string `json:"releaseNotesUrl"`
|
||||
Builds []GCOMPackage `json:"-"`
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/packaging"
|
||||
"github.com/grafana/grafana/pkg/build/versions"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_constructURL(t *testing.T) {
|
||||
type args struct {
|
||||
product string
|
||||
pth string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "cleans .. sequence", args: args{"..", ".."}, want: "https://grafana.com/api", wantErr: false},
|
||||
{name: "doesn't clean anything - non malicious url", args: args{"foo", "bar"}, want: "https://grafana.com/api/foo/bar", wantErr: false},
|
||||
{name: "doesn't clean anything - three dots", args: args{"...", "..."}, want: "https://grafana.com/api/.../...", wantErr: false},
|
||||
{name: "cleans .", args: args{"..", ".."}, want: "https://grafana.com/api", wantErr: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := constructURL(tt.args.product, tt.args.pth)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("constructURL() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("constructURL() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilds(t *testing.T) {
|
||||
baseURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "dl.example.com",
|
||||
Path: path.Join("oss", "release"),
|
||||
}
|
||||
|
||||
version := "1.2.3"
|
||||
grafana := "grafana"
|
||||
packages := []packaging.BuildArtifact{
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "arm64",
|
||||
Ext: "deb",
|
||||
},
|
||||
{
|
||||
Distro: "rhel",
|
||||
Arch: "aarch64",
|
||||
Ext: "rpm",
|
||||
},
|
||||
{
|
||||
Distro: "linux",
|
||||
Arch: "arm64",
|
||||
Ext: "tar.gz",
|
||||
},
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "armhf",
|
||||
Ext: "deb",
|
||||
RaspberryPi: true,
|
||||
},
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "armhf",
|
||||
Ext: "deb",
|
||||
},
|
||||
{
|
||||
Distro: "linux",
|
||||
Arch: "armv7",
|
||||
Ext: "tar.gz",
|
||||
},
|
||||
{
|
||||
Distro: "windows",
|
||||
Arch: "amd64",
|
||||
Ext: "zip",
|
||||
},
|
||||
{
|
||||
Distro: "windows",
|
||||
Arch: "amd64",
|
||||
Ext: "msi",
|
||||
},
|
||||
}
|
||||
|
||||
expect := []GCOMPackage{
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana_1.2.3_arm64.deb",
|
||||
OS: "deb",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3-1.aarch64.rpm",
|
||||
OS: "rhel",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3.linux-arm64.tar.gz",
|
||||
OS: "linux",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-rpi_1.2.3_armhf.deb",
|
||||
OS: "deb",
|
||||
Arch: "armv6",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana_1.2.3_armhf.deb",
|
||||
OS: "deb",
|
||||
Arch: "armv7",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3.linux-armv7.tar.gz",
|
||||
OS: "linux",
|
||||
Arch: "armv7",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3.windows-amd64.zip",
|
||||
OS: "win",
|
||||
Arch: "amd64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3.windows-amd64.msi",
|
||||
OS: "win-installer",
|
||||
Arch: "amd64",
|
||||
},
|
||||
}
|
||||
|
||||
builds, err := Builds(baseURL, grafana, version, packages)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(expect), len(builds))
|
||||
|
||||
for i := range builds {
|
||||
t.Run(fmt.Sprintf("[%d/%d] %s", i+1, len(builds), expect[i].URL), func(t *testing.T) {
|
||||
assert.Equal(t, expect[i].URL, builds[i].URL)
|
||||
assert.Equal(t, expect[i].OS, builds[i].OS)
|
||||
assert.Equal(t, expect[i].Arch, builds[i].Arch)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildsWithPlus(t *testing.T) {
|
||||
baseURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "dl.example.com",
|
||||
Path: path.Join("oss", "release"),
|
||||
}
|
||||
|
||||
version := "1.2.3+example-01"
|
||||
grafana := "grafana"
|
||||
packages := []packaging.BuildArtifact{
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "arm64",
|
||||
Ext: "deb",
|
||||
},
|
||||
{
|
||||
Distro: "rhel",
|
||||
Arch: "aarch64",
|
||||
Ext: "rpm",
|
||||
},
|
||||
{
|
||||
Distro: "linux",
|
||||
Arch: "arm64",
|
||||
Ext: "tar.gz",
|
||||
},
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "armhf",
|
||||
Ext: "deb",
|
||||
RaspberryPi: true,
|
||||
},
|
||||
{
|
||||
Distro: "deb",
|
||||
Arch: "armhf",
|
||||
Ext: "deb",
|
||||
},
|
||||
{
|
||||
Distro: "linux",
|
||||
Arch: "armv7",
|
||||
Ext: "tar.gz",
|
||||
},
|
||||
{
|
||||
Distro: "windows",
|
||||
Arch: "amd64",
|
||||
Ext: "zip",
|
||||
},
|
||||
{
|
||||
Distro: "windows",
|
||||
Arch: "amd64",
|
||||
Ext: "msi",
|
||||
},
|
||||
}
|
||||
|
||||
expect := []GCOMPackage{
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana_1.2.3+example~01_arm64.deb",
|
||||
OS: "deb",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example~01-1.aarch64.rpm",
|
||||
OS: "rhel",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.linux-arm64.tar.gz",
|
||||
OS: "linux",
|
||||
Arch: "arm64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-rpi_1.2.3+example~01_armhf.deb",
|
||||
OS: "deb",
|
||||
Arch: "armv6",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana_1.2.3+example~01_armhf.deb",
|
||||
OS: "deb",
|
||||
Arch: "armv7",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.linux-armv7.tar.gz",
|
||||
OS: "linux",
|
||||
Arch: "armv7",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.windows-amd64.zip",
|
||||
OS: "win",
|
||||
Arch: "amd64",
|
||||
},
|
||||
{
|
||||
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.windows-amd64.msi",
|
||||
OS: "win-installer",
|
||||
Arch: "amd64",
|
||||
},
|
||||
}
|
||||
|
||||
builds, err := Builds(baseURL, grafana, version, packages)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(expect), len(builds))
|
||||
|
||||
for i := range builds {
|
||||
t.Run(fmt.Sprintf("[%d/%d] %s", i+1, len(builds), expect[i].URL), func(t *testing.T) {
|
||||
assert.Equal(t, expect[i].URL, builds[i].URL)
|
||||
assert.Equal(t, expect[i].OS, builds[i].OS)
|
||||
assert.Equal(t, expect[i].Arch, builds[i].Arch)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseURLs(t *testing.T) {
|
||||
f := "https://grafana.com/whats-new-in-v%[1]s-%[2]s"
|
||||
|
||||
smv := versions.Semver{
|
||||
Major: "1",
|
||||
Minor: "2",
|
||||
Patch: "3",
|
||||
}
|
||||
|
||||
conf := packageConf{
|
||||
Grafana: grafanaConf{
|
||||
WhatsNewURL: f,
|
||||
ReleaseNotesURL: "https://example.com",
|
||||
},
|
||||
}
|
||||
|
||||
expect := "https://grafana.com/whats-new-in-v1-2"
|
||||
|
||||
a, _, err := getReleaseURLs(smv, &conf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expect, a)
|
||||
}
|
||||
@@ -4,18 +4,9 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cmd"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var additionalCommands []*cli.Command = make([]*cli.Command, 0, 5)
|
||||
|
||||
//nolint:unused
|
||||
func registerAppCommand(c *cli.Command) {
|
||||
additionalCommands = append(additionalCommands, c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// TODO change the registerer if the user is running using a JSON file etc
|
||||
for k, v := range cmd.Artifacts {
|
||||
@@ -25,272 +16,6 @@ func main() {
|
||||
}
|
||||
|
||||
app := cmd.GlobalCLI.App()
|
||||
artifactsCommand := cmd.GlobalCLI.ArtifactsCommand()
|
||||
artifactsCommand.Subcommands = cli.Commands{
|
||||
{
|
||||
Name: "storybook",
|
||||
Usage: "[ARCHIVED] Publish Grafana storybook",
|
||||
Action: PublishStorybookAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "storybook-bucket",
|
||||
Value: "grafana-storybook",
|
||||
Usage: "Google Cloud Storage bucket for storybooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "static-assets",
|
||||
Usage: "[ARCHIVED] Publish Grafana static assets",
|
||||
Action: PublishStaticAssetsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "static-assets-bucket",
|
||||
Value: "grafana-static-assets",
|
||||
Usage: "Google Cloud Storage bucket for static assets",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "static-asset-editions",
|
||||
Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "packages",
|
||||
Usage: "[ARCHIVED] Publish Grafana packages",
|
||||
Action: PublishArtifactsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringSliceFlag{
|
||||
Name: "artifacts-editions",
|
||||
Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"),
|
||||
Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-dest-bucket",
|
||||
Value: "grafana-downloads-enterprise2",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-security-prefix",
|
||||
Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "docker",
|
||||
Usage: "[ARCHIVED] Handle Grafana Docker images",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "fetch",
|
||||
Usage: "Fetch Grafana Docker images",
|
||||
ArgsUsage: "[version]",
|
||||
Action: util.MaxArgCountWrapper(1, FetchImages),
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "npm",
|
||||
Usage: "[ARCHIVED] Handle Grafana npm packages",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "release",
|
||||
Usage: "Release npm packages",
|
||||
ArgsUsage: "[version]",
|
||||
Action: NpmReleaseAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "store",
|
||||
Usage: "Store npm packages tarball",
|
||||
Action: NpmStoreAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "retrieve",
|
||||
Usage: "Retrieve npm packages tarball",
|
||||
Action: NpmRetrieveAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Commands = append(app.Commands, []*cli.Command{
|
||||
{
|
||||
Name: "e2e-tests",
|
||||
Usage: "[ARCHIVED] Run Grafana e2e tests",
|
||||
Action: EndToEndTests,
|
||||
Flags: []cli.Flag{
|
||||
&triesFlag,
|
||||
&cli.IntFlag{
|
||||
Name: "port",
|
||||
Value: 3001,
|
||||
Usage: "Specify the server port",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "suite",
|
||||
Usage: "Specify the end-to-end tests suite to be used",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "grafana-server",
|
||||
Usage: "Specify the server host",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "video",
|
||||
Value: "true",
|
||||
Usage: "Specify if videos should be recorded",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "upload-cdn",
|
||||
Usage: "[ARCHIVED] Upload public/* to a cdn bucket",
|
||||
Action: UploadCDN,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "publish-metrics",
|
||||
Usage: "[ARCHIVED] Publish a set of metrics from stdin",
|
||||
ArgsUsage: "<api-key>",
|
||||
Action: util.MaxArgCountWrapper(1, PublishMetrics),
|
||||
},
|
||||
{
|
||||
Name: "verify-drone",
|
||||
Usage: "[ARCHIVED] Verify Drone configuration",
|
||||
Action: VerifyDrone,
|
||||
},
|
||||
{
|
||||
Name: "store-storybook",
|
||||
Usage: "[ARCHIVED] Stores storybook to GCS buckets",
|
||||
Action: StoreStorybook,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "deployment",
|
||||
Usage: "Kind of deployment (e.g. canary/latest)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "verify-storybook",
|
||||
Usage: "[ARCHIVED] Integrity check for storybook build",
|
||||
Action: VerifyStorybook,
|
||||
},
|
||||
{
|
||||
Name: "upload-packages",
|
||||
Usage: "[ARCHIVED] Upload Grafana packages",
|
||||
Action: UploadPackages,
|
||||
Flags: []cli.Flag{
|
||||
&jobsFlag,
|
||||
&editionFlag,
|
||||
&cli.BoolFlag{
|
||||
Name: "enterprise2",
|
||||
Usage: "Declare if the edition is enterprise2",
|
||||
},
|
||||
},
|
||||
},
|
||||
artifactsCommand,
|
||||
{
|
||||
Name: "publish",
|
||||
Usage: "[ARCHIVED] Publish packages to Grafana com and repositories",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "grafana-com",
|
||||
Usage: "Publish packages to grafana.com",
|
||||
Action: GrafanaCom,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&buildIDFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "src-bucket",
|
||||
Value: "grafana-downloads",
|
||||
Usage: "Google Cloud Storage bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "github",
|
||||
Usage: "Publish packages to GitHub releases",
|
||||
Action: PublishGithub,
|
||||
Flags: []cli.Flag{
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "path",
|
||||
Usage: "Path to the asset to be published",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Required: true,
|
||||
Usage: "GitHub repository",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Release tag (default from metadata)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "create",
|
||||
Usage: "Create release if it doesn't exist",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "aws",
|
||||
Usage: "Publish image to AWS Marketplace releases",
|
||||
Action: PublishAwsMarketplace,
|
||||
Flags: []cli.Flag{
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Release version (default from metadata)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "image",
|
||||
Required: true,
|
||||
Usage: "Name of the image to be released",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Required: true,
|
||||
Usage: "AWS Marketplace ECR repository",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "product",
|
||||
Required: true,
|
||||
Usage: "AWS Marketplace product identifier",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}...)
|
||||
|
||||
app.Commands = append(app.Commands, additionalCommands...)
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatalln(err)
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/npm"
|
||||
)
|
||||
|
||||
func NpmRetrieveAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
tag := c.String("tag")
|
||||
if tag == "" {
|
||||
return fmt.Errorf("no tag version specified, exitting")
|
||||
}
|
||||
|
||||
if strings.Contains(tag, "security") {
|
||||
log.Printf("skipping npm publish because version '%s' has 'security'", tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
prereleaseBucket := strings.TrimSpace(os.Getenv("PRERELEASE_BUCKET"))
|
||||
if prereleaseBucket == "" {
|
||||
return cli.Exit("the environment variable PRERELEASE_BUCKET must be set", 1)
|
||||
}
|
||||
|
||||
err := npm.FetchNpmPackages(c.Context, tag, prereleaseBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NpmStoreAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
tag := c.String("tag")
|
||||
if tag == "" {
|
||||
return fmt.Errorf("no tag version specified, exiting")
|
||||
}
|
||||
|
||||
if strings.Contains(tag, "security") {
|
||||
log.Printf("skipping npm publish because version '%s' has 'security'", tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
prereleaseBucket := strings.TrimSpace(os.Getenv("PRERELEASE_BUCKET"))
|
||||
if prereleaseBucket == "" {
|
||||
return cli.Exit("the environment variable PRERELEASE_BUCKET must be set", 1)
|
||||
}
|
||||
|
||||
err := npm.StoreNpmPackages(c.Context, tag, prereleaseBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NpmReleaseAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
tag := c.String("tag")
|
||||
if tag == "" {
|
||||
return fmt.Errorf("no tag version specified, exitting")
|
||||
}
|
||||
|
||||
if strings.Contains(tag, "security") {
|
||||
log.Printf("skipping npm publish because version '%s' has 'security'", tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := npm.PublishNpmPackages(c.Context, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/env"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PublishArtifactsAction Action implements the sub-command "publish-artifacts".
|
||||
func PublishArtifactsAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
security := c.Bool("security")
|
||||
var securityDestBucket, enterprise2SecurityPrefix string
|
||||
|
||||
artifactsEditions, err := env.RequireListWithEnvFallback(c, "artifacts-editions", "ARTIFACTS_EDITIONS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if security {
|
||||
securityDestBucket, err = env.RequireStringWithEnvFallback(c, "security-dest-bucket", "SECURITY_DEST_BUCKET")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enterprise2SecurityPrefix, err = env.RequireStringWithEnvFallback(c, "enterprise2-security-prefix", "ENTERPRISE2_SECURITY_PREFIX")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("error connecting to gcp, %q", err)
|
||||
}
|
||||
|
||||
cfg := publishConfig{
|
||||
srcBucket: c.String("src-bucket"),
|
||||
destBucket: c.String("dest-bucket"),
|
||||
enterprise2DestBucket: c.String("enterprise2-dest-bucket"),
|
||||
enterprise2SecurityPrefix: enterprise2SecurityPrefix,
|
||||
security: security,
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
if cfg.security {
|
||||
cfg.destBucket = securityDestBucket
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, edition := range artifactsEditions {
|
||||
switch edition {
|
||||
case "oss", "enterprise":
|
||||
err = copyArtifacts(c, gcs, cfg, edition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "enterprise2":
|
||||
err = copyEnterprise2Artifacts(c, gcs, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Printf("unrecognised artifacts edition: %s\n", edition)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyArtifacts(c *cli.Context, gcs *storage.Client, cfg publishConfig, edition string) error {
|
||||
bucket := gcs.Bucket(cfg.destBucket)
|
||||
destURL := edition
|
||||
if !cfg.security {
|
||||
destURL = filepath.Join(destURL, "release")
|
||||
}
|
||||
log.Printf("Copying downloads for %s, from %s bucket to %s bucket", edition, cfg.srcBucket, destURL)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/downloads/v%s/%s/release", cfg.tag, edition), bucket, destURL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully copied downloads.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyEnterprise2Artifacts(c *cli.Context, gcs *storage.Client, cfg publishConfig) error {
|
||||
bucket := gcs.Bucket(cfg.enterprise2DestBucket)
|
||||
var prefix string
|
||||
if cfg.security {
|
||||
prefix = cfg.enterprise2SecurityPrefix
|
||||
}
|
||||
destURL := fmt.Sprintf("enterprise2/%srelease", prefix)
|
||||
log.Printf("Copying downloads for enterprise2, from %s bucket to %s bucket", cfg.srcBucket, destURL)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/downloads-enterprise2/v%s/enterprise2/release", cfg.tag), bucket, destURL); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
"github.com/aws/aws-sdk-go/service/marketplacecatalog"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
)
|
||||
|
||||
const (
|
||||
marketplaceChangeSetName = "Add new version"
|
||||
marketplaceCatalogId = "AWSMarketplace"
|
||||
marketplaceRegistryId = "709825985650"
|
||||
marketplaceRegistryRegion = "us-east-1"
|
||||
marketplaceRegistryUrl = "709825985650.dkr.ecr.us-east-1.amazonaws.com"
|
||||
marketplaceRequestsUrl = "https://aws.amazon.com/marketplace/management/requests/"
|
||||
releaseNotesTemplateUrl = "https://grafana.com/docs/grafana/latest/release-notes/release-notes-${TAG}/"
|
||||
helmChartsUrl = "https://grafana.github.io/helm-charts/"
|
||||
docsUrl = "https://grafana.com/docs/grafana/latest/enterprise/license/"
|
||||
imagePlatform = "linux/amd64"
|
||||
|
||||
publishAwsMarketplaceTestKey publishAwsMarketplaceTestKeyType = "test-client"
|
||||
)
|
||||
|
||||
var (
|
||||
errEmptyVersion = errors.New(`failed to retrieve release version from metadata, use "--version" to set it manually`)
|
||||
)
|
||||
|
||||
type publishAwsMarketplaceTestKeyType string
|
||||
|
||||
type publishAwsMarketplaceFlags struct {
|
||||
dryRun bool
|
||||
version string
|
||||
repo string
|
||||
image string
|
||||
product string
|
||||
}
|
||||
|
||||
type AwsMarketplacePublishingService struct {
|
||||
auth string
|
||||
docker AwsMarketplaceDocker
|
||||
ecr AwsMarketplaceRegistry
|
||||
mkt AwsMarketplaceCatalog
|
||||
}
|
||||
|
||||
type AwsMarketplaceDocker interface {
|
||||
ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error)
|
||||
ImageTag(ctx context.Context, source string, target string) error
|
||||
ImagePush(ctx context.Context, image string, options image.PushOptions) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
type AwsMarketplaceRegistry interface {
|
||||
GetAuthorizationTokenWithContext(ctx context.Context, input *ecr.GetAuthorizationTokenInput, opts ...request.Option) (*ecr.GetAuthorizationTokenOutput, error)
|
||||
}
|
||||
|
||||
type AwsMarketplaceCatalog interface {
|
||||
DescribeEntityWithContext(ctx context.Context, input *marketplacecatalog.DescribeEntityInput, opts ...request.Option) (*marketplacecatalog.DescribeEntityOutput, error)
|
||||
StartChangeSetWithContext(ctx context.Context, input *marketplacecatalog.StartChangeSetInput, opts ...request.Option) (*marketplacecatalog.StartChangeSetOutput, error)
|
||||
}
|
||||
|
||||
func PublishAwsMarketplace(ctx *cli.Context) error {
|
||||
f, err := getPublishAwsMarketplaceFlags(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.version == "" {
|
||||
return errEmptyVersion
|
||||
}
|
||||
|
||||
svc, err := getAwsMarketplacePublishingService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.Context.Value(publishAwsMarketplaceTestKey) != nil {
|
||||
svc = ctx.Context.Value(publishAwsMarketplaceTestKey).(*AwsMarketplacePublishingService)
|
||||
}
|
||||
|
||||
fmt.Println("Logging in to AWS Marketplace registry")
|
||||
err = svc.Login(ctx.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Retrieving image '%s:%s' from Docker Hub\n", f.image, f.version)
|
||||
err = svc.PullImage(ctx.Context, f.image, f.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Renaming image '%s:%s' to '%s/%s:%s'\n", f.image, f.version, marketplaceRegistryUrl, f.repo, f.version)
|
||||
err = svc.TagImage(ctx.Context, f.image, f.repo, f.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !f.dryRun {
|
||||
fmt.Printf("Pushing image '%s/%s:%s' to the AWS Marketplace ECR\n", marketplaceRegistryUrl, f.repo, f.version)
|
||||
err = svc.PushToMarketplace(ctx.Context, f.repo, f.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Dry-Run: Pushing image '%s/%s:%s' to the AWS Marketplace ECR\n", marketplaceRegistryUrl, f.repo, f.version)
|
||||
}
|
||||
|
||||
fmt.Printf("Retrieving product identifier for product '%s'\n", f.product)
|
||||
pid, err := svc.GetProductIdentifier(ctx.Context, f.product)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !f.dryRun {
|
||||
fmt.Printf("Releasing to product, you can view the progress of the release on %s\n", marketplaceRequestsUrl)
|
||||
return svc.ReleaseToProduct(ctx.Context, pid, f.repo, f.version)
|
||||
} else {
|
||||
fmt.Printf("Dry-Run: Releasing to product, you can view the progress of the release on %s\n", marketplaceRequestsUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAwsMarketplacePublishingService() (*AwsMarketplacePublishingService, error) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mySession := session.Must(session.NewSession())
|
||||
ecr := ecr.New(mySession, aws.NewConfig().WithRegion(marketplaceRegistryRegion))
|
||||
mkt := marketplacecatalog.New(mySession, aws.NewConfig().WithRegion(marketplaceRegistryRegion))
|
||||
return &AwsMarketplacePublishingService{
|
||||
docker: cli,
|
||||
ecr: ecr,
|
||||
mkt: mkt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) Login(ctx context.Context) error {
|
||||
out, err := s.ecr.GetAuthorizationTokenWithContext(ctx, &ecr.GetAuthorizationTokenInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.auth = *out.AuthorizationData[0].AuthorizationToken
|
||||
authData, err := base64.StdEncoding.DecodeString(s.auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authString := strings.Split(string(authData), ":")
|
||||
authData, err = json.Marshal(registry.AuthConfig{
|
||||
Username: authString[0],
|
||||
Password: authString[1],
|
||||
})
|
||||
s.auth = base64.StdEncoding.EncodeToString(authData)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) PullImage(ctx context.Context, img string, version string) error {
|
||||
reader, err := s.docker.ImagePull(ctx, fmt.Sprintf("%s:%s", img, version), image.PullOptions{
|
||||
Platform: imagePlatform,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(os.Stdout, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = reader.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) TagImage(ctx context.Context, image string, repo string, version string) error {
|
||||
err := s.docker.ImageTag(ctx, fmt.Sprintf("%s:%s", image, version), fmt.Sprintf("%s/%s:%s", marketplaceRegistryUrl, repo, version))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) PushToMarketplace(ctx context.Context, repo string, version string) error {
|
||||
reader, err := s.docker.ImagePush(ctx, fmt.Sprintf("%s/%s:%s", marketplaceRegistryUrl, repo, version), image.PushOptions{
|
||||
RegistryAuth: s.auth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(os.Stdout, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = reader.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) GetProductIdentifier(ctx context.Context, product string) (string, error) {
|
||||
out, err := s.mkt.DescribeEntityWithContext(ctx, &marketplacecatalog.DescribeEntityInput{
|
||||
EntityId: aws.String(product),
|
||||
Catalog: aws.String(marketplaceCatalogId),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return *out.EntityIdentifier, nil
|
||||
}
|
||||
|
||||
func (s *AwsMarketplacePublishingService) ReleaseToProduct(ctx context.Context, pid string, repo string, version string) error {
|
||||
_, err := s.mkt.StartChangeSetWithContext(ctx, &marketplacecatalog.StartChangeSetInput{
|
||||
Catalog: aws.String(marketplaceCatalogId),
|
||||
ChangeSetName: aws.String(marketplaceChangeSetName),
|
||||
ChangeSet: []*marketplacecatalog.Change{
|
||||
buildAwsMarketplaceChangeSet(pid, repo, version),
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func getPublishAwsMarketplaceFlags(ctx *cli.Context) (*publishAwsMarketplaceFlags, error) {
|
||||
metadata, err := config.GenerateMetadata(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := ctx.String("version")
|
||||
if version == "" && metadata.GrafanaVersion != "" {
|
||||
version = metadata.GrafanaVersion
|
||||
}
|
||||
image := ctx.String("image")
|
||||
repo := ctx.String("repo")
|
||||
product := ctx.String("product")
|
||||
dryRun := ctx.Bool("dry-run")
|
||||
return &publishAwsMarketplaceFlags{
|
||||
dryRun: dryRun,
|
||||
version: version,
|
||||
image: image,
|
||||
repo: repo,
|
||||
product: product,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildAwsMarketplaceReleaseNotesUrl(version string) string {
|
||||
sanitizedVersion := strings.ReplaceAll(version, ".", "-")
|
||||
return strings.ReplaceAll(releaseNotesTemplateUrl, "${TAG}", sanitizedVersion)
|
||||
}
|
||||
|
||||
func buildAwsMarketplaceChangeSet(entityId string, repo string, version string) *marketplacecatalog.Change {
|
||||
return &marketplacecatalog.Change{
|
||||
ChangeType: aws.String("AddDeliveryOptions"),
|
||||
Entity: &marketplacecatalog.Entity{
|
||||
Type: aws.String("ContainerProduct@1.0"),
|
||||
Identifier: aws.String(entityId),
|
||||
},
|
||||
Details: aws.String(buildAwsMarketplaceVersionDetails(repo, version)),
|
||||
}
|
||||
}
|
||||
|
||||
func buildAwsMarketplaceVersionDetails(repo string, version string) string {
|
||||
releaseNotesUrl := buildAwsMarketplaceReleaseNotesUrl(version)
|
||||
return fmt.Sprintf(`{
|
||||
"Version": {
|
||||
"ReleaseNotes": "Release notes are available on the website %s",
|
||||
"VersionTitle": "v%s"
|
||||
},
|
||||
"DeliveryOptions": [
|
||||
{
|
||||
"Details": {
|
||||
"EcrDeliveryOptionDetails": {
|
||||
"DeploymentResources": [
|
||||
{
|
||||
"Name": "Helm Charts",
|
||||
"Url": "%s"
|
||||
}
|
||||
],
|
||||
"CompatibleServices": ["EKS", "ECS", "ECS-Anywhere", "EKS-Anywhere"],
|
||||
"ContainerImages": ["%s/%s:%s"],
|
||||
"Description": "Grafana Enterprise can be installed using the official Grafana Helm chart repository. The repository is available on Github: %s",
|
||||
"UsageInstructions": "You can apply your Grafana Enterprise license to a new or existing Grafana Enterprise deployment by updating a configuration setting or environment variable. Your Grafana instance must be deployed on AWS, or have network access to AWS. For more information, see %s"
|
||||
}
|
||||
},
|
||||
"DeliveryOptionTitle": "Helm Chart"
|
||||
}
|
||||
]
|
||||
}`, releaseNotesUrl, version, helmChartsUrl, marketplaceRegistryUrl, repo, version, helmChartsUrl, docsUrl)
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
"github.com/aws/aws-sdk-go/service/marketplacecatalog"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type awsPublishTestCase struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError error
|
||||
errorContains string
|
||||
expectedOutput string
|
||||
mockedService *AwsMarketplacePublishingService
|
||||
}
|
||||
|
||||
func TestPublishAwsMarketplace(t *testing.T) {
|
||||
t.Setenv("DRONE_BUILD_EVENT", "promote")
|
||||
t.Setenv("DRONE_TAG", "v1.0.0")
|
||||
t.Setenv("DRONE_COMMIT", "abcdefgh")
|
||||
testApp := setupPublishAwsMarketplaceTests(t)
|
||||
errShouldNotCallMock := errors.New("shouldn't call")
|
||||
|
||||
testCases := []awsPublishTestCase{
|
||||
{
|
||||
name: "try to publish without required flags",
|
||||
errorContains: `Required flags "image, repo, product" not set`,
|
||||
},
|
||||
{
|
||||
name: "try to publish without credentials",
|
||||
args: []string{"--image", "test/test", "--repo", "test/test", "--product", "test", "--version", "1.0.0"},
|
||||
mockedService: &AwsMarketplacePublishingService{
|
||||
ecr: &mockAwsMarketplaceRegistry{
|
||||
GetAuthorizationTokenWithContextError: credentials.ErrNoValidProvidersFoundInChain,
|
||||
},
|
||||
},
|
||||
expectedError: credentials.ErrNoValidProvidersFoundInChain,
|
||||
},
|
||||
{
|
||||
name: "try to publish with valid credentials and nonexisting version",
|
||||
args: []string{"--image", "test/test", "--repo", "test/test", "--product", "test", "--version", "1.0.0"},
|
||||
mockedService: &AwsMarketplacePublishingService{
|
||||
ecr: &mockAwsMarketplaceRegistry{},
|
||||
docker: &mockAwsMarketplaceDocker{},
|
||||
mkt: &mockAwsMarketplaceCatalog{},
|
||||
},
|
||||
expectedOutput: "Releasing to product",
|
||||
},
|
||||
{
|
||||
name: "try to publish with valid credentials and existing version",
|
||||
args: []string{"--image", "test/test", "--repo", "test/test", "--product", "test", "--version", "1.0.0"},
|
||||
mockedService: &AwsMarketplacePublishingService{
|
||||
ecr: &mockAwsMarketplaceRegistry{},
|
||||
docker: &mockAwsMarketplaceDocker{},
|
||||
mkt: &mockAwsMarketplaceCatalog{},
|
||||
},
|
||||
expectedOutput: "Releasing to product",
|
||||
},
|
||||
{
|
||||
name: "dry run with invalid credentials",
|
||||
args: []string{"--dry-run", "--image", "test/test", "--repo", "test/test", "--product", "test", "--version", "1.0.0"},
|
||||
mockedService: &AwsMarketplacePublishingService{
|
||||
ecr: &mockAwsMarketplaceRegistry{
|
||||
GetAuthorizationTokenWithContextError: credentials.ErrNoValidProvidersFoundInChain,
|
||||
},
|
||||
},
|
||||
expectedError: credentials.ErrNoValidProvidersFoundInChain,
|
||||
},
|
||||
{
|
||||
name: "dry run with valid credentials",
|
||||
args: []string{"--dry-run", "--image", "test/test", "--repo", "test/test", "--product", "test", "--version", "1.0.0"},
|
||||
mockedService: &AwsMarketplacePublishingService{
|
||||
ecr: &mockAwsMarketplaceRegistry{},
|
||||
docker: &mockAwsMarketplaceDocker{
|
||||
ImagePushError: errShouldNotCallMock,
|
||||
},
|
||||
mkt: &mockAwsMarketplaceCatalog{
|
||||
StartChangeSetWithContextError: errShouldNotCallMock,
|
||||
},
|
||||
},
|
||||
expectedOutput: "Dry-Run: Releasing to product",
|
||||
},
|
||||
}
|
||||
|
||||
if os.Getenv("DRONE_COMMIT") == "" {
|
||||
// this test only works locally due to Drone environment
|
||||
testCases = append(testCases,
|
||||
awsPublishTestCase{
|
||||
name: "try to publish without version",
|
||||
args: []string{"--image", "test/test", "--repo", "test/test", "--product", "test"},
|
||||
expectedError: errEmptyVersion,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), publishAwsMarketplaceTestKey, test.mockedService)
|
||||
args := []string{"run"}
|
||||
args = append(args, test.args...)
|
||||
out, err := captureStdout(t, func() error {
|
||||
return testApp.RunContext(ctx, args)
|
||||
})
|
||||
if test.expectedOutput != "" {
|
||||
assert.Contains(t, out, test.expectedOutput)
|
||||
}
|
||||
if test.expectedError != nil || test.errorContains != "" {
|
||||
assert.Error(t, err)
|
||||
if test.expectedError != nil {
|
||||
assert.ErrorIs(t, err, test.expectedError)
|
||||
}
|
||||
if test.errorContains != "" {
|
||||
assert.ErrorContains(t, err, test.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupPublishAwsMarketplaceTests(t *testing.T) *cli.App {
|
||||
t.Helper()
|
||||
testApp := cli.NewApp()
|
||||
testApp.Action = PublishAwsMarketplace
|
||||
testApp.Flags = []cli.Flag{
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Release version (default from metadata)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "image",
|
||||
Required: true,
|
||||
Usage: "Name of the image to be released",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Required: true,
|
||||
Usage: "AWS Marketplace ECR repository",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "product",
|
||||
Required: true,
|
||||
Usage: "AWS Marketplace product identifier",
|
||||
},
|
||||
}
|
||||
return testApp
|
||||
}
|
||||
|
||||
type mockAwsMarketplaceDocker struct {
|
||||
ImagePullError error
|
||||
ImageTagError error
|
||||
ImagePushError error
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceDocker) ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader([]byte(""))), m.ImagePullError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceDocker) ImageTag(ctx context.Context, source string, target string) error {
|
||||
return m.ImageTagError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceDocker) ImagePush(ctx context.Context, image string, options image.PushOptions) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader([]byte(""))), m.ImagePushError
|
||||
}
|
||||
|
||||
type mockAwsMarketplaceRegistry struct {
|
||||
GetAuthorizationTokenWithContextError error
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceRegistry) GetAuthorizationTokenWithContext(ctx context.Context, input *ecr.GetAuthorizationTokenInput, opts ...request.Option) (*ecr.GetAuthorizationTokenOutput, error) {
|
||||
return &ecr.GetAuthorizationTokenOutput{
|
||||
AuthorizationData: []*ecr.AuthorizationData{
|
||||
{
|
||||
AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString([]byte("username:password"))),
|
||||
},
|
||||
},
|
||||
}, m.GetAuthorizationTokenWithContextError
|
||||
}
|
||||
|
||||
type mockAwsMarketplaceCatalog struct {
|
||||
DescribeEntityWithContextError error
|
||||
StartChangeSetWithContextError error
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceCatalog) DescribeEntityWithContext(ctx context.Context, input *marketplacecatalog.DescribeEntityInput, opts ...request.Option) (*marketplacecatalog.DescribeEntityOutput, error) {
|
||||
return &marketplacecatalog.DescribeEntityOutput{
|
||||
EntityIdentifier: aws.String("productid"),
|
||||
}, m.DescribeEntityWithContextError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceCatalog) StartChangeSetWithContext(ctx context.Context, input *marketplacecatalog.StartChangeSetInput, opts ...request.Option) (*marketplacecatalog.StartChangeSetOutput, error) {
|
||||
return &marketplacecatalog.StartChangeSetOutput{}, m.StartChangeSetWithContextError
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package main
|
||||
|
||||
type publishConfig struct {
|
||||
tag string
|
||||
srcBucket string
|
||||
destBucket string
|
||||
enterprise2DestBucket string
|
||||
enterprise2SecurityPrefix string
|
||||
staticAssetsBucket string
|
||||
staticAssetEditions []string
|
||||
storybookBucket string
|
||||
security bool
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
)
|
||||
|
||||
type githubRepositoryService interface {
|
||||
GetReleaseByTag(ctx context.Context, owner string, repo string, tag string) (*github.RepositoryRelease, *github.Response, error)
|
||||
CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error)
|
||||
UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error)
|
||||
DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error)
|
||||
ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opt *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error)
|
||||
}
|
||||
|
||||
type githubRepo struct {
|
||||
owner string
|
||||
name string
|
||||
}
|
||||
|
||||
type publishGithubFlags struct {
|
||||
create bool
|
||||
dryRun bool
|
||||
tag string
|
||||
repo *githubRepo
|
||||
artifactPath string
|
||||
}
|
||||
|
||||
var (
|
||||
newGithubClient = githubRepositoryClient
|
||||
errTokenIsEmpty = errors.New("the environment variable GH_TOKEN must be set")
|
||||
errTagIsEmpty = errors.New(`failed to retrieve release tag from metadata, use "--tag" to set it manually`)
|
||||
errReleaseNotFound = errors.New(`release not found, use "--create" to create the release`)
|
||||
)
|
||||
|
||||
func PublishGithub(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
token := os.Getenv("GH_TOKEN")
|
||||
f, err := getPublishGithubFlags(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.tag == "" {
|
||||
return errTagIsEmpty
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return errTokenIsEmpty
|
||||
}
|
||||
|
||||
if f.dryRun {
|
||||
return runPublishGithubDryRun(f, token, c)
|
||||
}
|
||||
|
||||
client := newGithubClient(ctx, token)
|
||||
release, res, err := client.GetReleaseByTag(ctx, f.repo.owner, f.repo.name, f.tag)
|
||||
if err != nil && res.StatusCode != 404 {
|
||||
return err
|
||||
}
|
||||
|
||||
if release == nil {
|
||||
if f.create {
|
||||
release, _, err = client.CreateRelease(ctx, f.repo.owner, f.repo.name, &github.RepositoryRelease{TagName: &f.tag})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errReleaseNotFound
|
||||
}
|
||||
}
|
||||
|
||||
artifactName := path.Base(f.artifactPath)
|
||||
file, err := os.Open(f.artifactPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assetsPage := 1
|
||||
foundAsset := false
|
||||
for {
|
||||
assets, resp, err := client.ListReleaseAssets(ctx, f.repo.owner, f.repo.name, *release.ID, &github.ListOptions{
|
||||
Page: assetsPage,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, asset := range assets {
|
||||
if asset.GetName() == artifactName {
|
||||
fmt.Printf("Found existing artifact with the name '%s'. Deleting that now.\n", artifactName)
|
||||
if _, err := client.DeleteReleaseAsset(ctx, f.repo.owner, f.repo.name, asset.GetID()); err != nil {
|
||||
return fmt.Errorf("failed to delete already existing asset: %w", err)
|
||||
}
|
||||
foundAsset = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if resp.NextPage <= assetsPage || foundAsset {
|
||||
break
|
||||
}
|
||||
assetsPage = resp.NextPage
|
||||
}
|
||||
|
||||
asset, _, err := client.UploadReleaseAsset(ctx, f.repo.owner, f.repo.name, *release.ID, &github.UploadOptions{Name: artifactName}, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Asset '%s' uploaded to release '%s' on repository '%s/%s'\nDownload: %s\n", *asset.Name, f.tag, f.repo.owner, f.repo.name, *asset.BrowserDownloadURL)
|
||||
return nil
|
||||
}
|
||||
|
||||
func githubRepositoryClient(ctx context.Context, token string) githubRepositoryService {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
|
||||
client := github.NewClient(tc)
|
||||
return client.Repositories
|
||||
}
|
||||
|
||||
func getPublishGithubFlags(c *cli.Context) (*publishGithubFlags, error) {
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := c.Value("tag").(string)
|
||||
if tag == "" && metadata.GrafanaVersion != "" {
|
||||
tag = fmt.Sprintf("v%s", metadata.GrafanaVersion)
|
||||
}
|
||||
fullRepo := c.Value("repo").(string)
|
||||
dryRun := c.Value("dry-run").(bool)
|
||||
owner := strings.Split(fullRepo, "/")[0]
|
||||
name := strings.Split(fullRepo, "/")[1]
|
||||
create := c.Value("create").(bool)
|
||||
artifactPath := c.Value("path").(string)
|
||||
if artifactPath == "" {
|
||||
artifactPath = fmt.Sprintf("grafana-enterprise2-%s-amd64.img", metadata.GrafanaVersion)
|
||||
fmt.Printf("path argument is not provided, resolving to default %s...\n", artifactPath)
|
||||
}
|
||||
return &publishGithubFlags{
|
||||
artifactPath: artifactPath,
|
||||
create: create,
|
||||
dryRun: dryRun,
|
||||
tag: tag,
|
||||
repo: &githubRepo{
|
||||
owner: owner,
|
||||
name: name,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func runPublishGithubDryRun(f *publishGithubFlags, token string, c *cli.Context) error {
|
||||
client := newGithubClient(c.Context, token)
|
||||
fmt.Println("Dry-Run: Retrieving release on repository by tag")
|
||||
release, res, err := client.GetReleaseByTag(c.Context, f.repo.owner, f.repo.name, f.tag)
|
||||
if err != nil && res.StatusCode != 404 {
|
||||
fmt.Println("Dry-Run: Github communication error:\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if release == nil {
|
||||
if f.create {
|
||||
fmt.Println("Dry-Run: Release doesn't exist and --create is enabled, so it would try to create the release")
|
||||
} else {
|
||||
fmt.Println("Dry-Run: Release doesn't exist and --create is disabled, so it would fail with error")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
artifactName := path.Base(f.artifactPath)
|
||||
fmt.Printf("Dry-Run: Opening file for release: %s\n", f.artifactPath)
|
||||
_, err = os.Open(f.artifactPath)
|
||||
if err != nil {
|
||||
fmt.Println("Dry-Run: Error opening file\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Dry-Run: Would upload asset '%s' to release '%s' on repo '%s/%s' and return download URL if successful\n", artifactName, f.tag, f.repo.owner, f.repo.name)
|
||||
return nil
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
)
|
||||
|
||||
type githubPublishTestCases struct {
|
||||
name string
|
||||
args []string
|
||||
token string
|
||||
expectedError error
|
||||
errorContains string
|
||||
expectedOutput string
|
||||
mockedService *mockGithubRepositoryServiceImpl
|
||||
}
|
||||
|
||||
var mockGithubRepositoryService = &mockGithubRepositoryServiceImpl{}
|
||||
|
||||
func mockGithubRepositoryClient(context.Context, string) githubRepositoryService {
|
||||
return mockGithubRepositoryService
|
||||
}
|
||||
|
||||
func TestPublishGithub(t *testing.T) {
|
||||
t.Setenv("DRONE_BUILD_EVENT", "promote")
|
||||
t.Setenv("DRONE_TAG", "v1.0.0")
|
||||
t.Setenv("DRONE_COMMIT", "abcdefgh")
|
||||
testApp, testPath := setupPublishGithubTests(t)
|
||||
mockErrUnauthorized := errors.New("401")
|
||||
|
||||
testCases := []githubPublishTestCases{
|
||||
{
|
||||
name: "try to publish without required flags",
|
||||
errorContains: `Required flags "path, repo" not set`,
|
||||
},
|
||||
{
|
||||
name: "try to publish without token",
|
||||
args: []string{"--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
expectedError: errTokenIsEmpty,
|
||||
},
|
||||
{
|
||||
name: "try to publish with invalid token",
|
||||
token: "invalid",
|
||||
args: []string{"--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: mockErrUnauthorized},
|
||||
expectedError: mockErrUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "try to publish with valid token and nonexisting tag with create disabled",
|
||||
token: "valid",
|
||||
args: []string{"--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: errReleaseNotFound},
|
||||
expectedError: errReleaseNotFound,
|
||||
},
|
||||
{
|
||||
name: "try to publish with valid token and nonexisting tag with create enabled",
|
||||
token: "valid",
|
||||
args: []string{"--path", testPath, "--repo", "test/test", "--tag", "v1.0.0", "--create"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: errReleaseNotFound},
|
||||
},
|
||||
{
|
||||
name: "try to publish with valid token and existing tag",
|
||||
token: "valid",
|
||||
args: []string{"--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
},
|
||||
{
|
||||
name: "dry run with invalid token",
|
||||
token: "invalid",
|
||||
args: []string{"--dry-run", "--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: mockErrUnauthorized},
|
||||
expectedOutput: "Github communication error",
|
||||
},
|
||||
{
|
||||
name: "dry run with valid token and nonexisting tag with create disabled",
|
||||
token: "valid",
|
||||
args: []string{"--dry-run", "--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: errReleaseNotFound},
|
||||
expectedOutput: "Release doesn't exist",
|
||||
},
|
||||
{
|
||||
name: "dry run with valid token and nonexisting tag with create enabled",
|
||||
token: "valid",
|
||||
args: []string{"--dry-run", "--path", testPath, "--repo", "test/test", "--tag", "v1.0.0", "--create"},
|
||||
mockedService: &mockGithubRepositoryServiceImpl{tagErr: errReleaseNotFound},
|
||||
expectedOutput: "Would upload asset",
|
||||
},
|
||||
{
|
||||
name: "dry run with valid token and existing tag",
|
||||
token: "valid",
|
||||
args: []string{"--dry-run", "--path", testPath, "--repo", "test/test", "--tag", "v1.0.0"},
|
||||
expectedOutput: "Would upload asset",
|
||||
},
|
||||
}
|
||||
|
||||
if os.Getenv("DRONE_COMMIT") == "" {
|
||||
// this test only works locally due to Drone environment
|
||||
testCases = append(testCases,
|
||||
githubPublishTestCases{
|
||||
name: "try to publish without tag",
|
||||
args: []string{"--path", testPath, "--repo", "test/test"},
|
||||
expectedError: errTagIsEmpty,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.token != "" {
|
||||
t.Setenv("GH_TOKEN", test.token)
|
||||
}
|
||||
if test.mockedService != nil {
|
||||
mockGithubRepositoryService = test.mockedService
|
||||
} else {
|
||||
mockGithubRepositoryService = &mockGithubRepositoryServiceImpl{}
|
||||
}
|
||||
args := []string{"run"}
|
||||
args = append(args, test.args...)
|
||||
out, err := captureStdout(t, func() error {
|
||||
return testApp.Run(args)
|
||||
})
|
||||
if test.expectedOutput != "" {
|
||||
assert.Contains(t, out, test.expectedOutput)
|
||||
}
|
||||
if test.expectedError != nil || test.errorContains != "" {
|
||||
assert.Error(t, err)
|
||||
if test.expectedError != nil {
|
||||
assert.ErrorIs(t, err, test.expectedError)
|
||||
}
|
||||
if test.errorContains != "" {
|
||||
assert.ErrorContains(t, err, test.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupPublishGithubTests(t *testing.T) (*cli.App, string) {
|
||||
t.Helper()
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testPath := filepath.Dir(ex)
|
||||
|
||||
newGithubClient = mockGithubRepositoryClient
|
||||
|
||||
testApp := cli.NewApp()
|
||||
testApp.Action = PublishGithub
|
||||
testApp.Flags = []cli.Flag{
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "path",
|
||||
Required: true,
|
||||
Usage: "Path to the asset to be published",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Required: true,
|
||||
Usage: "Github repository",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Release tag (default from metadata)ß",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "create",
|
||||
Usage: "Create release if it doesn't exist",
|
||||
},
|
||||
}
|
||||
return testApp, testPath
|
||||
}
|
||||
|
||||
func captureStdout(t *testing.T, fn func() error) (string, error) {
|
||||
t.Helper()
|
||||
rescueStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
err := fn()
|
||||
werr := w.Close()
|
||||
if werr != nil {
|
||||
return "", err
|
||||
}
|
||||
out, _ := io.ReadAll(r)
|
||||
os.Stdout = rescueStdout
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
type mockGithubRepositoryServiceImpl struct {
|
||||
tagErr error
|
||||
createErr error
|
||||
uploadErr error
|
||||
}
|
||||
|
||||
func (m *mockGithubRepositoryServiceImpl) GetReleaseByTag(ctx context.Context, owner string, repo string, tag string) (*github.RepositoryRelease, *github.Response, error) {
|
||||
var release *github.RepositoryRelease
|
||||
res := &github.Response{Response: &http.Response{}}
|
||||
if m.tagErr == nil {
|
||||
releaseID := int64(1)
|
||||
release = &github.RepositoryRelease{ID: &releaseID}
|
||||
} else if errors.Is(m.tagErr, errReleaseNotFound) {
|
||||
res.StatusCode = 404
|
||||
}
|
||||
return release, res, m.tagErr
|
||||
}
|
||||
|
||||
func (m *mockGithubRepositoryServiceImpl) CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error) {
|
||||
releaseID := int64(1)
|
||||
return &github.RepositoryRelease{ID: &releaseID}, &github.Response{}, m.createErr
|
||||
}
|
||||
|
||||
func (m *mockGithubRepositoryServiceImpl) UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error) {
|
||||
assetName := "test"
|
||||
assetUrl := "testurl.com.br"
|
||||
return &github.ReleaseAsset{Name: &assetName, BrowserDownloadURL: &assetUrl}, &github.Response{}, m.uploadErr
|
||||
}
|
||||
|
||||
func (m *mockGithubRepositoryServiceImpl) DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockGithubRepositoryServiceImpl) ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opts *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error) {
|
||||
resp := github.Response{}
|
||||
resp.LastPage = 1
|
||||
resp.FirstPage = 1
|
||||
resp.NextPage = 0
|
||||
return []*github.ReleaseAsset{}, &resp, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/metrics"
|
||||
)
|
||||
|
||||
func PublishMetrics(c *cli.Context) error {
|
||||
apiKey := c.Args().Get(0)
|
||||
|
||||
input, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return cli.Exit(fmt.Sprintf("Reading from stdin failed: %s", err), 1)
|
||||
}
|
||||
|
||||
reMetrics := regexp.MustCompile(`(?ms)^Metrics: (\{.+\})`)
|
||||
ms := reMetrics.FindSubmatch(input)
|
||||
if len(ms) == 0 {
|
||||
return cli.Exit(fmt.Sprintf("Input on wrong format: %q", string(input)), 1)
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
if err := json.Unmarshal(ms[1], &m); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("decoding metrics failed: %s", err), 1)
|
||||
}
|
||||
|
||||
log.Printf("Received metrics %+v", m)
|
||||
|
||||
if err := metrics.Publish(m, apiKey); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("publishing metrics failed: %s", err), 1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/env"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PublishStaticAssetsAction Action implements the sub-command "artifacts static-assets".
|
||||
func PublishStaticAssetsAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
staticAssetEditions, err := env.RequireListWithEnvFallback(c, "static-asset-editions", "STATIC_ASSET_EDITIONS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("error connecting to gcp, %q", err)
|
||||
}
|
||||
|
||||
cfg := publishConfig{
|
||||
srcBucket: c.String("src-bucket"),
|
||||
staticAssetsBucket: c.String("static-assets-bucket"),
|
||||
staticAssetEditions: staticAssetEditions,
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(cfg.staticAssetsBucket)
|
||||
|
||||
for _, edition := range staticAssetEditions {
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/static-assets/%s/%s", edition, cfg.tag), bucket, fmt.Sprintf("%s/%s", edition, cfg.tag)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/grafana/grafana/pkg/build/versions"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PublishStorybookAction Action implements the sub-command "publish-artifacts".
|
||||
func PublishStorybookAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("error connecting to gcp, %q", err)
|
||||
}
|
||||
|
||||
cfg := publishConfig{
|
||||
srcBucket: c.String("src-bucket"),
|
||||
storybookBucket: c.String("storybook-bucket"),
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(cfg.storybookBucket)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/storybook/v%s", cfg.tag), bucket, cfg.tag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if latest, err := isLatest(cfg); err == nil && latest {
|
||||
log.Printf("Copying storybooks to latest...")
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/storybook/v%s", cfg.tag), bucket, "latest"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLatest(cfg publishConfig) (bool, error) {
|
||||
stableVersion, err := versions.GetLatestVersion(versions.LatestStableVersionURL)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
isLatest, err := versions.IsGreaterThanOrEqual(cfg.tag, stableVersion)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return isLatest, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
)
|
||||
|
||||
// StoreStorybook implements the sub-command "store-storybook".
|
||||
func StoreStorybook(c *cli.Context) error {
|
||||
deployment := c.String("deployment")
|
||||
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storybookBucket := buildConfig.Buckets.Storybook
|
||||
srcPath := buildConfig.Buckets.StorybookSrcDir
|
||||
srcPath = filepath.Join(srcPath, deployment)
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(storybookBucket)
|
||||
|
||||
if err := gcs.DeleteDir(c.Context, bucket, srcPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully cleaned source: %s/%s\n", storybookBucket, srcPath)
|
||||
|
||||
if err := gcs.CopyLocalDir(c.Context, "packages/grafana-ui/dist/storybook", bucket, srcPath, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully stored storybook to: %s/%s!\n", storybookBucket, srcPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
)
|
||||
|
||||
// UploadCDN implements the sub-command "upload-cdn".
|
||||
func UploadCDN(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := metadata.GrafanaVersion
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
edition := os.Getenv("EDITION")
|
||||
log.Printf("Uploading Grafana CDN Assets, version %s, %s edition...", version, edition)
|
||||
|
||||
editionPath := ""
|
||||
|
||||
switch config.Edition(edition) {
|
||||
case config.EditionOSS:
|
||||
editionPath = "grafana-oss"
|
||||
case config.EditionEnterprise:
|
||||
editionPath = "grafana"
|
||||
case config.EditionEnterprise2:
|
||||
editionPath = os.Getenv("ENTERPRISE2_CDN_PATH")
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized edition %q", edition))
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bucket := gcs.Bucket(buildConfig.Buckets.CDNAssets)
|
||||
srcPath := buildConfig.Buckets.CDNAssetsDir
|
||||
srcPath = filepath.Join(srcPath, editionPath, version)
|
||||
|
||||
if err := gcs.DeleteDir(c.Context, bucket, srcPath); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Successfully cleaned source: %s/%s\n", buildConfig.Buckets.CDNAssets, srcPath)
|
||||
|
||||
if err := gcs.CopyLocalDir(c.Context, "./public", bucket, srcPath, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully uploaded cdn static assets to: %s/%s!\n", buildConfig.Buckets.CDNAssets, srcPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/grafana/grafana/pkg/build/droneutil"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
)
|
||||
|
||||
// PackageRegexp returns a regexp for matching packages corresponding to a certain Grafana edition.
|
||||
func PackageRegexp(edition config.Edition) *regexp.Regexp {
|
||||
var sfx string
|
||||
switch edition {
|
||||
case config.EditionOSS:
|
||||
case config.EditionEnterprise:
|
||||
sfx = "-enterprise"
|
||||
case config.EditionEnterprise2:
|
||||
sfx = "-enterprise2"
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized edition %q", edition))
|
||||
}
|
||||
rePkg, err := regexp.Compile(fmt.Sprintf(`^grafana%s(?:-rpi)?[-_][^-_]+.*$`, sfx))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to compile regexp: %s", err))
|
||||
}
|
||||
|
||||
return rePkg
|
||||
}
|
||||
|
||||
const releaseFolder = "release"
|
||||
const mainFolder = "main"
|
||||
const releaseBranchFolder = "prerelease"
|
||||
|
||||
type uploadConfig struct {
|
||||
config.Config
|
||||
|
||||
edition config.Edition
|
||||
versionMode config.VersionMode
|
||||
gcpKey string
|
||||
distDir string
|
||||
versionFolder string
|
||||
}
|
||||
|
||||
// UploadPackages implements the sub-command "upload-packages".
|
||||
func UploadPackages(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
gcpKeyB64 := strings.TrimSpace(os.Getenv("GCP_KEY"))
|
||||
if gcpKeyB64 == "" {
|
||||
return cli.Exit("the environment variable GCP_KEY must be set", 1)
|
||||
}
|
||||
gcpKeyB, err := base64.StdEncoding.DecodeString(gcpKeyB64)
|
||||
if err != nil {
|
||||
return cli.Exit("failed to base64 decode $GCP_KEY", 1)
|
||||
}
|
||||
gcpKey := string(gcpKeyB)
|
||||
|
||||
distDir, err := filepath.Abs("dist")
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := metadata.GrafanaVersion
|
||||
|
||||
releaseMode, err := metadata.GetReleaseMode()
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
releaseModeConfig, err := config.GetBuildConfig(releaseMode.Mode)
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
var edition config.Edition
|
||||
if e, ok := os.LookupEnv("EDITION"); ok {
|
||||
edition = config.Edition(e)
|
||||
}
|
||||
|
||||
if c.Bool("enterprise2") {
|
||||
edition = config.EditionEnterprise2
|
||||
}
|
||||
|
||||
if edition == "" {
|
||||
return fmt.Errorf("both EDITION envvar and '--enterprise2' flag are missing. At least one of those is required")
|
||||
}
|
||||
|
||||
// TODO: Verify config values
|
||||
cfg := uploadConfig{
|
||||
Config: config.Config{
|
||||
Version: version,
|
||||
Bucket: releaseModeConfig.Buckets.Artifacts,
|
||||
},
|
||||
edition: edition,
|
||||
versionMode: releaseMode.Mode,
|
||||
gcpKey: gcpKey,
|
||||
distDir: distDir,
|
||||
}
|
||||
|
||||
event, err := droneutil.GetDroneEventFromEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.edition == config.EditionEnterprise2 {
|
||||
cfg.Bucket, err = bucketForEnterprise2(releaseModeConfig, event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cfg.versionFolder, err = getVersionFolder(cfg, event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := uploadPackages(cfg); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
log.Println("Successfully uploaded packages!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Corner case for custom enterprise2 mode
|
||||
func bucketForEnterprise2(releaseModeConfig *config.BuildConfig, event string) (string, error) {
|
||||
if event == config.Custom {
|
||||
buildConfig, err := config.GetBuildConfig(config.ReleaseBranchMode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buildConfig.Buckets.ArtifactsEnterprise2, nil
|
||||
}
|
||||
|
||||
if releaseModeConfig.Buckets.ArtifactsEnterprise2 != "" {
|
||||
return releaseModeConfig.Buckets.ArtifactsEnterprise2, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("enterprise2 bucket var doesn't exist")
|
||||
}
|
||||
|
||||
func getVersionFolder(cfg uploadConfig, event string) (string, error) {
|
||||
switch cfg.versionMode {
|
||||
case config.TagMode, config.CloudMode:
|
||||
return releaseFolder, nil
|
||||
case config.MainMode, config.DownstreamMode:
|
||||
return mainFolder, nil
|
||||
case config.ReleaseBranchMode:
|
||||
return releaseBranchFolder, nil
|
||||
default:
|
||||
// Corner case for custom enterprise2 mode
|
||||
if event == config.Custom && cfg.versionMode == config.Enterprise2Mode {
|
||||
return releaseFolder, nil
|
||||
}
|
||||
return "", fmt.Errorf("unrecognized version mode: %s", cfg.versionMode)
|
||||
}
|
||||
}
|
||||
|
||||
func uploadPackages(cfg uploadConfig) error {
|
||||
log.Printf("Uploading Grafana packages, version %s, %s edition, %s mode...\n", cfg.Version, cfg.edition,
|
||||
cfg.versionMode)
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("couldn't activate service account, err: %w", err)
|
||||
}
|
||||
|
||||
edition := strings.ToLower(string(cfg.edition))
|
||||
|
||||
var sfx string
|
||||
switch cfg.edition {
|
||||
case config.EditionOSS:
|
||||
case config.EditionEnterprise:
|
||||
sfx = "-enterprise"
|
||||
case config.EditionEnterprise2:
|
||||
sfx = "-enterprise2"
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized edition %q", cfg.edition))
|
||||
}
|
||||
matches, err := filepath.Glob(filepath.Join(cfg.distDir, fmt.Sprintf("grafana%s*", sfx)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list packages: %w", err)
|
||||
}
|
||||
fpaths := []string{}
|
||||
rePkg := PackageRegexp(cfg.edition)
|
||||
for _, fpath := range matches {
|
||||
fname := filepath.Base(fpath)
|
||||
if strings.Contains(fname, "latest") || !rePkg.MatchString(fname) {
|
||||
log.Printf("Ignoring file %q\n", fpath)
|
||||
continue
|
||||
}
|
||||
|
||||
fpaths = append(fpaths, fpath)
|
||||
}
|
||||
|
||||
var tag, gcsPath string
|
||||
droneTag := strings.TrimSpace(os.Getenv("DRONE_TAG"))
|
||||
if droneTag != "" {
|
||||
tag = droneTag
|
||||
gcsPath = fmt.Sprintf("gs://%s/%s/%s/%s", cfg.Bucket, tag, edition, cfg.versionFolder)
|
||||
} else {
|
||||
gcsPath = fmt.Sprintf("gs://%s/%s/%s/", cfg.Bucket, edition, cfg.versionFolder)
|
||||
}
|
||||
log.Printf("Uploading %d file(s) to GCS (%s)...\n", len(fpaths), gcsPath)
|
||||
|
||||
args := []string{"-m", "cp"}
|
||||
args = append(args, fpaths...)
|
||||
args = append(args, gcsPath)
|
||||
cmd := exec.Command("gsutil", args...)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to upload files to GCS: %s", output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
)
|
||||
|
||||
func Test_getVersionFolder(t *testing.T) {
|
||||
type args struct {
|
||||
cfg uploadConfig
|
||||
event string
|
||||
versionFolder string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
err error
|
||||
}{
|
||||
{"tag mode", args{uploadConfig{versionMode: config.TagMode}, "", releaseFolder}, nil},
|
||||
{"main mode", args{uploadConfig{versionMode: config.MainMode}, "", mainFolder}, nil},
|
||||
{"downstream mode", args{uploadConfig{versionMode: config.DownstreamMode}, "", mainFolder}, nil},
|
||||
{"release branch mode", args{uploadConfig{versionMode: config.ReleaseBranchMode}, "", releaseBranchFolder}, nil},
|
||||
{"enterprise pro mode", args{uploadConfig{versionMode: config.Enterprise2Mode}, config.Custom, releaseFolder}, nil},
|
||||
{"cloud mode", args{uploadConfig{versionMode: config.CloudMode}, "", releaseFolder}, nil},
|
||||
{"unrecognised version mode", args{uploadConfig{versionMode: "foo"}, config.Custom, ""}, errors.New("")},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
versionMode, err := getVersionFolder(tt.args.cfg, tt.args.event)
|
||||
if tt.err != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
require.Equal(t, versionMode, tt.args.versionFolder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_checkForEnterprise2Edition(t *testing.T) {
|
||||
type args struct {
|
||||
releaseModeConfig *config.BuildConfig
|
||||
event string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{"event is not custom", args{releaseModeConfig: &config.BuildConfig{Buckets: config.Buckets{ArtifactsEnterprise2: "dummy"}}}, "dummy", nil},
|
||||
{"event is not custom and string is empty", args{releaseModeConfig: &config.BuildConfig{Buckets: config.Buckets{ArtifactsEnterprise2: ""}}}, "", fmt.Errorf("enterprise2 bucket var doesn't exist")},
|
||||
{"event is custom", args{releaseModeConfig: nil, event: "custom"}, "grafana-downloads-enterprise2", nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := bucketForEnterprise2(tt.args.releaseModeConfig, tt.args.event)
|
||||
if tt.err != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "bucketForEnterprise2(%v, %v)", tt.args.releaseModeConfig, tt.args.event)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/drone/drone-cli/drone/lint"
|
||||
"github.com/drone/drone-cli/drone/starlark"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
cliv1 "github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
||||
)
|
||||
|
||||
func VerifyDrone(c *cli.Context) error {
|
||||
const yml = ".drone.yml"
|
||||
const backup = ".drone.yml.bak"
|
||||
|
||||
if err := fsutil.CopyFile(yml, backup); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to copy %s to %s: %s", yml, backup, err), 1)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Remove(yml); err != nil {
|
||||
log.Printf("Failed to rename %s to %s", backup, yml)
|
||||
}
|
||||
if err := os.Rename(backup, yml); err != nil {
|
||||
log.Printf("Failed to rename %s to %s", backup, yml)
|
||||
}
|
||||
}()
|
||||
|
||||
flags := &flag.FlagSet{}
|
||||
for _, flag := range starlark.Command.Flags {
|
||||
flag.Apply(flags)
|
||||
}
|
||||
if err := flags.Set("format", "true"); err != nil {
|
||||
return err
|
||||
}
|
||||
cStarlark := cliv1.NewContext(cliv1.NewApp(), flags, nil)
|
||||
action := starlark.Command.Action.(func(*cliv1.Context))
|
||||
action(cStarlark)
|
||||
|
||||
if err := verifyYAML(yml, backup); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flags = &flag.FlagSet{}
|
||||
for _, flag := range lint.Command.Flags {
|
||||
flag.Apply(flags)
|
||||
}
|
||||
err := flags.Set("trusted", "true")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cLint := cliv1.NewContext(cliv1.NewApp(), flags, nil)
|
||||
actionE := lint.Command.Action.(func(*cliv1.Context) error)
|
||||
if err := actionE(cLint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("%s is valid", yml)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readConfig(fpath string) ([]map[string]any, error) {
|
||||
//nolint:gosec
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return nil, cli.Exit(fmt.Sprintf("failed to read %s: %s", fpath, err), 1)
|
||||
}
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Println("error closing file", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// The YAML stream may contain multiple pipeline configurations, read them all
|
||||
dec := yaml.NewDecoder(f)
|
||||
var c []map[string]any
|
||||
for {
|
||||
var m map[string]any
|
||||
if err := dec.Decode(&m); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, cli.Exit(fmt.Sprintf("Failed to decode %s: %s", fpath, err), 1)
|
||||
}
|
||||
|
||||
if m["kind"] == "signature" {
|
||||
log.Printf("Ignoring a signature")
|
||||
continue
|
||||
}
|
||||
|
||||
c = append(c, m)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func verifyYAML(yml, backup string) error {
|
||||
log.Printf("Comparing %s and %s", yml, backup)
|
||||
|
||||
c1, err := readConfig(yml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c2, err := readConfig(backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cmp.Equal(c1, c2) {
|
||||
return cli.Exit(fmt.Sprintf("%s is out of sync with .drone.star - regenerate it with drone starlark convert",
|
||||
yml), 1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Package verifystorybook contains the sub-command "verify-storybook".
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// VerifyStorybook Action implements the sub-command "verify-storybook".
|
||||
func VerifyStorybook(c *cli.Context) error {
|
||||
const grafanaDir = "."
|
||||
|
||||
paths := []string{
|
||||
"packages/grafana-ui/dist/storybook/index.html",
|
||||
"packages/grafana-ui/dist/storybook/iframe.html"}
|
||||
for _, p := range paths {
|
||||
exists, err := fsutil.Exists(filepath.Join(grafanaDir, p))
|
||||
if err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to verify Storybook build: %s", err), 1)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("failed to verify Storybook build, missing %q", p)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Successfully verified Storybook integrity")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user