magefiles/resources.go (79 lines of code) (raw):
//go:build mage
package main
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/magefile/mage/mg"
"github.com/samber/lo"
"github.com/sourcegraph/conc/iter"
"gitlab.com/gitlab-org/gitlab-runner/magefiles/build"
)
type Resources mg.Namespace
// Verify verifies that the resources exported match the expected blueprint
// blueprints are expected to be exported to the `out/release_artifacts/<typ>.json` file
func (Resources) Verify(typ string) error {
rows, err := verify(build.ReleaseArtifactsPath(typ))
renderTable(rows)
return err
}
func verify(f string) ([]table.Row, error) {
if _, err := os.Stat(f); os.IsNotExist(err) {
return nil, err
}
b, err := os.ReadFile(f)
if err != nil {
return nil, err
}
var m []map[string]string
if err := json.Unmarshal(b, &m); err != nil {
return nil, err
}
c := lo.Map(m, func(m map[string]string, _ int) build.Component {
required, _ := strconv.ParseBool(m["Required"])
return build.NewComponent(
m["Value"],
m["Type"],
m["Description"],
required,
)
})
checked, _ := build.CheckComponents(c)
rows := build.RowsFromCheckedComponents(checked)
errs := lo.FilterMap(lo.Values(checked), func(t lo.Tuple2[string, error], _ int) (error, bool) {
return t.B, t.B != nil
})
if len(errs) == 0 {
return rows, nil
}
return rows, errors.New("there were errors in the checked resources")
}
func renderTable(rows []table.Row) {
t := table.NewWriter()
t.AppendHeader(table.Row{"Resources status"})
t.AppendSeparator()
t.AppendRow(table.Row{"Resource", "Type", "Exists"})
t.AppendSeparator()
t.AppendRows(rows)
fmt.Println(t.Render())
}
// VerifyAll verifies that all resources exported match the expected blueprint
// blueprints are expected to be exported to the `out/release_artifacts/*.json` files
func (Resources) VerifyAll() error {
// TODO: verify that the resources exported match the expected blueprint
// Currently, we rely on each job to export its artifacts. This is great, however if a job
// doesn't export its artifacts correctly we could miss some resources.
// We need to generate blueprints in the verify stage of the pipeline and then export the artifacts
// and compare them to the ones actually exported by the jobs. This is not very straightforward
// so let's do it in a separate MR, later.
dir := filepath.Dir(build.ReleaseArtifactsPath(""))
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
mapper := iter.Mapper[os.DirEntry, []table.Row]{
MaxGoroutines: config.Concurrency,
}
rows, err := mapper.MapErr(entries, func(entry *os.DirEntry) ([]table.Row, error) {
if (*entry).IsDir() {
return nil, nil
}
f := (*entry).Name()
return verify(filepath.Join(dir, f))
})
renderTable(lo.Flatten(rows))
return err
}