mirror of https://github.com/artifacthub/hub.git
710 lines
20 KiB
Go
710 lines
20 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"path"
|
||
"path/filepath"
|
||
"reflect"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/Masterminds/semver/v3"
|
||
"github.com/artifacthub/hub/internal/hub"
|
||
"github.com/artifacthub/hub/internal/pkg"
|
||
"github.com/artifacthub/hub/internal/tracker/source/generic"
|
||
"github.com/artifacthub/hub/internal/tracker/source/helm"
|
||
"github.com/artifacthub/hub/internal/tracker/source/helmplugin"
|
||
"github.com/artifacthub/hub/internal/tracker/source/krew"
|
||
"github.com/artifacthub/hub/internal/tracker/source/olm"
|
||
"github.com/artifacthub/hub/internal/tracker/source/tekton"
|
||
"github.com/hashicorp/go-multierror"
|
||
"github.com/spf13/cobra"
|
||
"helm.sh/helm/v3/pkg/chart/loader"
|
||
"helm.sh/helm/v3/pkg/plugin"
|
||
)
|
||
|
||
const (
|
||
sepLen = 120
|
||
success = '✓'
|
||
failure = '✗'
|
||
warning = '!'
|
||
provided = "PROVIDED"
|
||
notProvided = "*** NOT PROVIDED ***"
|
||
)
|
||
|
||
// lintDesc represents the long description of the lint command.
|
||
var lintDesc = `Check the repository's packages are ready for Artifact Hub
|
||
|
||
Use this command to check that the packages in your repository are ready to be
|
||
listed on Artifact Hub. This command checks that the packages metadata provided
|
||
is valid and displays some information about the data that will be collected so
|
||
that you can verify everything looks right.`
|
||
|
||
var (
|
||
// errLintFailed indicates that the lint command failed. This happens
|
||
// when errors are found in any of the packages available in the path
|
||
// provided.
|
||
errLintFailed = errors.New("lint failed")
|
||
|
||
// errNoPackagesFound indicates that no packages were found in the provided
|
||
// path.
|
||
errNoPackagesFound = errors.New("no packages found")
|
||
)
|
||
|
||
// lintOptions represents the options that can be passed to the lint command.
|
||
type lintOptions struct {
|
||
// kind represents the repository kind.
|
||
kind string
|
||
|
||
// path represents the base path to walk looking for packages.
|
||
path string
|
||
}
|
||
|
||
// lintReport represents the results of checking all the packages found in the
|
||
// provided path. For each package, an entry is created with some information
|
||
// about the package and the errors found on it during the check.
|
||
type lintReport struct {
|
||
entries []*lintReportEntry
|
||
}
|
||
|
||
// lintReportEntry represents an entry of the lint report. A lint report
|
||
// entry contains a package and the errors found on it during the check.
|
||
type lintReportEntry struct {
|
||
pkg *hub.Package
|
||
path string
|
||
result *multierror.Error
|
||
}
|
||
|
||
// newLintCmd creates a new lint command.
|
||
func newLintCmd() *cobra.Command {
|
||
opts := &lintOptions{}
|
||
lintCmd := &cobra.Command{
|
||
Use: "lint",
|
||
Short: "Check the repository's packages are ready for Artifact Hub",
|
||
Long: lintDesc,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
return lint(opts, &output{cmd.OutOrStdout()})
|
||
},
|
||
}
|
||
lintCmd.Flags().StringVarP(&opts.kind, "kind", "k", "helm", "repository kind: argo-template, backstage, coredns, falco, gatekeeper, headlamp, helm, helm-plugin, inspektor-gadget, kcl, keda-scaler, keptn, knative-client-plugin, krew, kubearmor, kubewarden, kyverno, olm, opa, tbaction, tekton-task, tekton-pipeline, tekton-stepaction")
|
||
lintCmd.Flags().StringVarP(&opts.path, "path", "p", ".", "repository's packages path")
|
||
return lintCmd
|
||
}
|
||
|
||
// lint checks that the packages found in the path provided are ready to be
|
||
// listed on Artifact Hub. The resulting lint report will be printed to the
|
||
// output provided.
|
||
func lint(opts *lintOptions, out *output) error {
|
||
// Check all packages available in the path provided. Different kinds may
|
||
// use a specific linter. The linter will return a lint report that will be
|
||
// printed once the check has finished.
|
||
kind, err := hub.GetKindFromName(opts.kind)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var report *lintReport
|
||
switch kind {
|
||
case
|
||
hub.ArgoTemplate,
|
||
hub.Backstage,
|
||
hub.CoreDNS,
|
||
hub.Falco,
|
||
hub.Gatekeeper,
|
||
hub.Headlamp,
|
||
hub.InspektorGadget,
|
||
hub.KCL,
|
||
hub.KedaScaler,
|
||
hub.Keptn,
|
||
hub.KnativeClientPlugin,
|
||
hub.KubeArmor,
|
||
hub.Kubewarden,
|
||
hub.Kyverno,
|
||
hub.OPA,
|
||
hub.TBAction:
|
||
report = lintGeneric(opts.path, kind)
|
||
case hub.Helm:
|
||
report = lintHelm(opts.path)
|
||
case hub.HelmPlugin:
|
||
report = lintHelmPlugin(opts.path)
|
||
case hub.Krew:
|
||
report = lintKrew(opts.path)
|
||
case hub.OLM:
|
||
report = lintOLM(opts.path)
|
||
case hub.TektonTask, hub.TektonPipeline, hub.TektonStepAction:
|
||
report = lintTekton(opts.path, kind)
|
||
default:
|
||
return errors.New("kind not supported yet")
|
||
}
|
||
|
||
// Print lint report and return the corresponding error
|
||
if len(report.entries) == 0 {
|
||
return errNoPackagesFound
|
||
}
|
||
out.printReport(report)
|
||
for _, entry := range report.entries {
|
||
if entry.result.ErrorOrNil() != nil {
|
||
return errLintFailed
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// lintGeneric checks if the packages available in the path provided are ready
|
||
// to be processed by the generic tracker source and listed on Artifact Hub.
|
||
func lintGeneric(basePath string, kind hub.RepositoryKind) *lintReport {
|
||
report := &lintReport{}
|
||
|
||
// Walk the path provided looking for available packages
|
||
_ = filepath.Walk(basePath, func(pkgPath string, info os.FileInfo, err error) error {
|
||
// If an error is raised while visiting a path or the path is not a
|
||
// directory, we skip it
|
||
if err != nil || !info.IsDir() {
|
||
return nil
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
e := &lintReportEntry{
|
||
path: pkgPath,
|
||
}
|
||
|
||
// Get package version metadata and prepare entry package
|
||
mdFilePath := filepath.Join(pkgPath, hub.PackageMetadataFile)
|
||
md, err := pkg.GetPackageMetadata(kind, mdFilePath)
|
||
if err != nil {
|
||
if errors.Is(err, os.ErrNotExist) {
|
||
return nil
|
||
}
|
||
e.result = multierror.Append(e.result, err)
|
||
} else {
|
||
e.pkg, err = generic.PreparePackage(&hub.Repository{Kind: kind}, md, pkgPath)
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
}
|
||
}
|
||
|
||
report.entries = append(report.entries, e)
|
||
return nil
|
||
})
|
||
|
||
return report
|
||
}
|
||
|
||
// lintHelm checks if the Helm charts available in the path provided are ready
|
||
// to be processed by the Helm tracker source and listed on Artifact Hub.
|
||
func lintHelm(basePath string) *lintReport {
|
||
report := &lintReport{}
|
||
|
||
// Walk the path provided looking for available charts
|
||
_ = filepath.Walk(basePath, func(chartPath string, info os.FileInfo, err error) error {
|
||
// If an error is raised while visiting a path or the path is not a
|
||
// directory, we skip it
|
||
if err != nil || !info.IsDir() {
|
||
return nil
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
e := &lintReportEntry{
|
||
path: chartPath,
|
||
}
|
||
|
||
// Try loading chart in the current path (may or may not be found)
|
||
chrt, err := loader.LoadDir(chartPath)
|
||
if err != nil {
|
||
if chrt != nil && chrt.Metadata != nil {
|
||
// A chart was found in the current path, but it is not valid.
|
||
// We have enough information to keep checking other pieces of
|
||
// data, so we track the error and continue.
|
||
e.result = multierror.Append(e.result, err)
|
||
} else {
|
||
// A chart was not found in the current path
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// Prepare entry package from Helm chart information
|
||
e.pkg = &hub.Package{
|
||
Name: chrt.Metadata.Name,
|
||
Version: chrt.Metadata.Version,
|
||
LogoURL: chrt.Metadata.Icon,
|
||
Repository: &hub.Repository{Kind: hub.Helm},
|
||
}
|
||
helm.EnrichPackageFromChart(e.pkg, chrt)
|
||
err = helm.EnrichPackageFromAnnotations(e.pkg, chrt.Metadata.Annotations)
|
||
e.result = multierror.Append(e.result, err)
|
||
|
||
report.entries = append(report.entries, e)
|
||
return nil
|
||
})
|
||
|
||
return report
|
||
}
|
||
|
||
// lintHelmPlugin checks if the Helm plugins available in the path provided are
|
||
// ready to be processed by the Helm plugins tracker source and listed on
|
||
// Artifact Hub.
|
||
func lintHelmPlugin(basePath string) *lintReport {
|
||
report := &lintReport{}
|
||
|
||
// Walk the path provided looking for available plugins
|
||
_ = filepath.Walk(basePath, func(pkgPath string, info os.FileInfo, err error) error {
|
||
// If an error is raised while visiting a path or the path is not a
|
||
// directory, we skip it
|
||
if err != nil || !info.IsDir() {
|
||
return nil
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
e := &lintReportEntry{
|
||
path: pkgPath,
|
||
}
|
||
|
||
// Get Helm plugin metadata and prepare package
|
||
mdFilePath := filepath.Join(pkgPath, plugin.PluginFileName)
|
||
md, err := helmplugin.GetMetadata(mdFilePath)
|
||
if err != nil {
|
||
if errors.Is(err, os.ErrNotExist) {
|
||
return nil
|
||
}
|
||
e.result = multierror.Append(e.result, err)
|
||
} else {
|
||
repo := &hub.Repository{
|
||
Kind: hub.HelmPlugin,
|
||
URL: "https://github.com/user/repo/path",
|
||
}
|
||
e.pkg, err = helmplugin.PreparePackage(repo, md, pkgPath)
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
}
|
||
}
|
||
|
||
report.entries = append(report.entries, e)
|
||
return nil
|
||
})
|
||
|
||
return report
|
||
}
|
||
|
||
// lintKrew checks if the Krew plugins available in the path provided are ready
|
||
// to be processed by the Krew tracker source and listed on Artifact Hub.
|
||
func lintKrew(basePath string) *lintReport {
|
||
report := &lintReport{}
|
||
|
||
// Process plugins available in the path provided
|
||
pluginsPath := filepath.Join(basePath, "plugins")
|
||
pluginManifestFiles, err := os.ReadDir(pluginsPath)
|
||
if err != nil {
|
||
return report
|
||
}
|
||
for _, file := range pluginManifestFiles {
|
||
// Only process plugins files
|
||
if !file.Type().IsRegular() || filepath.Ext(file.Name()) != ".yaml" {
|
||
continue
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
pluginPath := filepath.Join(pluginsPath, file.Name())
|
||
e := &lintReportEntry{
|
||
path: pluginPath,
|
||
}
|
||
|
||
// Get Krew plugin manifest and prepare package
|
||
manifest, manifestRaw, err := krew.GetManifest(filepath.Join(pluginsPath, file.Name()))
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
} else {
|
||
e.pkg, err = krew.PreparePackage(&hub.Repository{Kind: hub.Krew}, manifest, manifestRaw)
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
}
|
||
}
|
||
|
||
report.entries = append(report.entries, e)
|
||
}
|
||
|
||
return report
|
||
}
|
||
|
||
// lintOLM checks if the OLM operators available in the path provided are ready
|
||
// to be processed by the OLM tracker source and listed on Artifact Hub.
|
||
func lintOLM(basePath string) *lintReport {
|
||
report := &lintReport{}
|
||
|
||
// Walk the path provided looking for available OLM operators
|
||
_ = filepath.Walk(basePath, func(pkgPath string, info os.FileInfo, err error) error {
|
||
// If an error is raised while visiting a path or the path is not a
|
||
// directory, we skip it
|
||
if err != nil || !info.IsDir() {
|
||
return nil
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
e := &lintReportEntry{
|
||
path: pkgPath,
|
||
}
|
||
|
||
// Get metadata and prepare package
|
||
md, err := olm.GetMetadata(pkgPath)
|
||
switch {
|
||
case err != nil:
|
||
e.result = multierror.Append(e.result, err)
|
||
case md == nil:
|
||
// Package manifest not found, not a package path
|
||
return nil
|
||
default:
|
||
repo := &hub.Repository{
|
||
Kind: hub.OLM,
|
||
}
|
||
e.pkg, err = olm.PreparePackage(repo, md)
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
}
|
||
}
|
||
|
||
report.entries = append(report.entries, e)
|
||
return nil
|
||
})
|
||
|
||
return report
|
||
}
|
||
|
||
// lintTekton checks if the Tekton tasks, pipelines or stepactions available in
|
||
// the path provided are ready to be processed by the Tekton tracker source and
|
||
// listed on Artifact Hub.
|
||
func lintTekton(basePath string, kind hub.RepositoryKind) *lintReport {
|
||
report := &lintReport{}
|
||
repository := &hub.Repository{
|
||
Kind: kind,
|
||
URL: "https://github.com/user/repo/path",
|
||
Data: json.RawMessage(fmt.Sprintf(`{"versioning": "%s"}`, hub.TektonDirBasedVersioning)),
|
||
}
|
||
|
||
// Read catalog path to get available packages
|
||
packages, err := os.ReadDir(basePath)
|
||
if err != nil {
|
||
return report
|
||
}
|
||
for _, p := range packages {
|
||
// If the path is not a directory, we skip it
|
||
if !p.IsDir() {
|
||
continue
|
||
}
|
||
|
||
// Read package versions
|
||
pkgName := p.Name()
|
||
pkgBasePath := path.Join(basePath, pkgName)
|
||
versions, err := os.ReadDir(pkgBasePath)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
for _, v := range versions {
|
||
// If the path is not a directory or a ~valid semver version, we skip it
|
||
if !p.IsDir() {
|
||
continue
|
||
}
|
||
sv, err := semver.NewVersion(v.Name())
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// Initialize report entry. If a package is found in the current path,
|
||
// errors found while processing it will be added to the report.
|
||
pkgPath := path.Join(pkgBasePath, v.Name())
|
||
e := &lintReportEntry{
|
||
path: pkgPath,
|
||
}
|
||
|
||
// Get package manifest
|
||
manifest, manifestRaw, err := tekton.GetManifest(kind, pkgName, pkgPath)
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
} else {
|
||
// Prepare package version
|
||
e.pkg, err = tekton.PreparePackage(&tekton.PreparePackageInput{
|
||
R: repository,
|
||
Tag: "",
|
||
Manifest: manifest,
|
||
ManifestRaw: manifestRaw,
|
||
BasePath: basePath,
|
||
PkgName: pkgName,
|
||
PkgPath: pkgPath,
|
||
PkgVersion: sv.String(),
|
||
})
|
||
if err != nil {
|
||
e.result = multierror.Append(e.result, err)
|
||
}
|
||
}
|
||
|
||
report.entries = append(report.entries, e)
|
||
}
|
||
}
|
||
|
||
return report
|
||
}
|
||
|
||
// output represents a wrapper around an io.Writer used to print lint reports.
|
||
type output struct {
|
||
io.Writer
|
||
}
|
||
|
||
// printReport prints the provided lint report to the receiver output.
|
||
func (out *output) printReport(report *lintReport) {
|
||
// Print packages checks results
|
||
for _, e := range report.entries {
|
||
// Setup minimal skeleton package if not provided
|
||
if e.pkg == nil {
|
||
e.pkg = &hub.Package{
|
||
Name: "name: ?",
|
||
Version: "version: ?",
|
||
}
|
||
}
|
||
|
||
// Header
|
||
var mark rune
|
||
if e.result.ErrorOrNil() != nil {
|
||
mark = failure
|
||
} else {
|
||
mark = success
|
||
}
|
||
fmt.Fprintf(out, "\n%s\n", strings.Repeat("-", sepLen))
|
||
fmt.Fprintf(out, "%c %s %s (%s)\n", mark, e.pkg.Name, e.pkg.Version, e.path)
|
||
fmt.Fprintf(out, "%s\n\n", strings.Repeat("-", sepLen))
|
||
|
||
// Details
|
||
if e.result.ErrorOrNil() == nil {
|
||
fmt.Fprintf(out, "Package lint SUCCEEDED!\n\n")
|
||
out.printPkgDetails(e.pkg)
|
||
} else {
|
||
fmt.Fprintf(out, "Package lint FAILED. %d error(s) occurred:\n\n", len(e.result.Errors))
|
||
for _, err := range e.result.Errors {
|
||
fmt.Fprintf(out, " * %s\n", strings.TrimSpace(err.Error()))
|
||
}
|
||
}
|
||
}
|
||
|
||
// Print footer summary
|
||
var pkgsWithErrors int
|
||
for _, e := range report.entries {
|
||
if e.result.ErrorOrNil() != nil {
|
||
pkgsWithErrors++
|
||
}
|
||
}
|
||
fmt.Fprintf(out, "\n%s\n", strings.Repeat("-", sepLen))
|
||
fmt.Fprintf(out, "\n%d package(s) found, %d package(s) with errors\n\n", len(report.entries), pkgsWithErrors)
|
||
}
|
||
|
||
// printPkgDetails prints the details of the package provided to the receiver
|
||
// output. A mark will be added to each of the fields to indicate if the value
|
||
// was provided or not.
|
||
func (out *output) printPkgDetails(pkg *hub.Package) {
|
||
// General
|
||
out.print("Name", pkg.Name)
|
||
out.print("Display name", pkg.DisplayName)
|
||
out.print("Version", pkg.Version)
|
||
out.print("App version", pkg.AppVersion)
|
||
out.print("Description", pkg.Description)
|
||
out.print("Keywords", pkg.Keywords)
|
||
out.print("License", pkg.License)
|
||
out.print("Logo URL", pkg.LogoURL)
|
||
out.print("Home URL", pkg.HomeURL)
|
||
out.print("Deprecated", strconv.FormatBool(pkg.Deprecated))
|
||
out.print("Pre-release", strconv.FormatBool(pkg.Prerelease))
|
||
out.print("Contains security updates", strconv.FormatBool(pkg.ContainsSecurityUpdates))
|
||
out.print("Provider", pkg.Provider)
|
||
|
||
// Readme
|
||
if pkg.Readme != "" {
|
||
fmt.Fprintf(out, "%c Readme: %s\n", success, provided)
|
||
} else {
|
||
fmt.Fprintf(out, "%c Readme: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Keywords
|
||
if len(pkg.Keywords) > 0 {
|
||
fmt.Fprintf(out, "%c Keywords:\n", success)
|
||
for _, keyword := range pkg.Keywords {
|
||
fmt.Fprintf(out, " - %s\n", keyword)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Keywords: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Links
|
||
if len(pkg.Links) > 0 {
|
||
fmt.Fprintf(out, "%c Links:\n", success)
|
||
for _, l := range pkg.Links {
|
||
fmt.Fprintf(out, " - Name: %s | URL: %s\n", l.Name, l.URL)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Links: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Maintainers
|
||
if len(pkg.Maintainers) > 0 {
|
||
fmt.Fprintf(out, "%c Maintainers:\n", success)
|
||
for _, m := range pkg.Maintainers {
|
||
fmt.Fprintf(out, " - Name: %s | Email: %s\n", m.Name, m.Email)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Maintainers: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Containers images
|
||
if len(pkg.ContainersImages) > 0 {
|
||
fmt.Fprintf(out, "%c Containers images:\n", success)
|
||
for _, i := range pkg.ContainersImages {
|
||
fmt.Fprintf(out, " - Name: %s | Image: %s\n", i.Name, i.Image)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Containers images: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Changes
|
||
if len(pkg.Changes) > 0 {
|
||
fmt.Fprintf(out, "%c Changes:\n", success)
|
||
for _, c := range pkg.Changes {
|
||
fmt.Fprintf(out, " - Kind: %s | Description: %s\n", c.Kind, c.Description)
|
||
if len(c.Links) > 0 {
|
||
fmt.Fprintf(out, " - Links:\n")
|
||
for _, l := range c.Links {
|
||
fmt.Fprintf(out, " - Name: %s | URL: %s\n", l.Name, l.URL)
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Changes: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Recommendations
|
||
if len(pkg.Recommendations) > 0 {
|
||
fmt.Fprintf(out, "%c Recommendations:\n", success)
|
||
for _, r := range pkg.Recommendations {
|
||
fmt.Fprintf(out, " - %s\n", r.URL)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Recommendations: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Screenshots
|
||
if len(pkg.Screenshots) > 0 {
|
||
fmt.Fprintf(out, "%c Screenshots:\n", success)
|
||
for _, s := range pkg.Screenshots {
|
||
fmt.Fprintf(out, " - Title: %s | URL: %s\n", s.Title, s.URL)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Screenshots: %s\n", warning, notProvided)
|
||
}
|
||
|
||
// Operator
|
||
out.print("Operator", strconv.FormatBool(pkg.IsOperator))
|
||
if pkg.IsOperator {
|
||
out.print("Operator capabilities", pkg.Capabilities)
|
||
out.print("CRDs", pkg.CRDs)
|
||
out.print("CRDs examples", pkg.CRDsExamples)
|
||
}
|
||
|
||
// Values specific to a repository kind
|
||
switch pkg.Repository.Kind {
|
||
case
|
||
hub.ArgoTemplate,
|
||
hub.Backstage,
|
||
hub.CoreDNS,
|
||
hub.Falco,
|
||
hub.Gatekeeper,
|
||
hub.Headlamp,
|
||
hub.InspektorGadget,
|
||
hub.KCL,
|
||
hub.KedaScaler,
|
||
hub.Keptn,
|
||
hub.KnativeClientPlugin,
|
||
hub.KubeArmor,
|
||
hub.Kubewarden,
|
||
hub.Kyverno,
|
||
hub.OPA,
|
||
hub.TBAction:
|
||
|
||
// Install
|
||
if pkg.Install != "" {
|
||
fmt.Fprintf(out, "%c Install: %s\n", success, provided)
|
||
} else {
|
||
fmt.Fprintf(out, "%c Install: %s\n", warning, notProvided)
|
||
}
|
||
|
||
switch pkg.Repository.Kind {
|
||
case hub.Falco:
|
||
// Rules files
|
||
fmt.Fprintf(out, "%c Rules: %s\n", success, provided)
|
||
for name := range pkg.Data[generic.FalcoRulesKey].(map[string]string) {
|
||
fmt.Fprintf(out, " - %s\n", name)
|
||
}
|
||
case hub.OPA:
|
||
// Policies files
|
||
fmt.Fprintf(out, "%c Policies: %s\n", success, provided)
|
||
for name := range pkg.Data[generic.OPAPoliciesKey].(map[string]string) {
|
||
fmt.Fprintf(out, " - %s\n", name)
|
||
}
|
||
}
|
||
case hub.Helm:
|
||
out.print("Sign key", pkg.SignKey)
|
||
|
||
// Values schema
|
||
if pkg.ValuesSchema != nil {
|
||
fmt.Fprintf(out, "%c Values schema: %s\n", success, provided)
|
||
} else {
|
||
fmt.Fprintf(out, "%c Values schema: %s\n", warning, notProvided)
|
||
}
|
||
case hub.Krew:
|
||
// Platforms
|
||
if v, ok := pkg.Data[krew.PlatformsKey]; ok {
|
||
platforms, ok := v.([]string)
|
||
if ok && len(platforms) > 0 {
|
||
fmt.Fprintf(out, "%c Platforms: %s\n", success, provided)
|
||
for _, platform := range platforms {
|
||
fmt.Fprintf(out, " - %s\n", platform)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Platforms: %s\n", warning, notProvided)
|
||
}
|
||
}
|
||
case hub.OLM:
|
||
out.print("Default channel", pkg.DefaultChannel)
|
||
|
||
// Channels
|
||
if len(pkg.Channels) > 0 {
|
||
fmt.Fprintf(out, "%c Channels:\n", success)
|
||
for _, channel := range pkg.Channels {
|
||
fmt.Fprintf(out, " - %s -> %s\n", channel.Name, channel.Version)
|
||
}
|
||
} else {
|
||
fmt.Fprintf(out, "%c Channels: %s\n", warning, notProvided)
|
||
}
|
||
}
|
||
}
|
||
|
||
// print is a helper function used to print the label and values provided with
|
||
// a success or warning mark that indicates if the value was provided or not.
|
||
func (out *output) print(label string, value interface{}) {
|
||
switch reflect.TypeOf(value).Kind() {
|
||
case reflect.String:
|
||
if value != "" {
|
||
fmt.Fprintf(out, "%c %s: %s\n", success, label, value)
|
||
} else {
|
||
fmt.Fprintf(out, "%c %s: %s\n", warning, label, notProvided)
|
||
}
|
||
case reflect.Ptr:
|
||
if !reflect.ValueOf(value).IsNil() {
|
||
fmt.Fprintf(out, "%c %s: %s\n", success, label, provided)
|
||
} else {
|
||
fmt.Fprintf(out, "%c %s: %s\n", warning, label, notProvided)
|
||
}
|
||
}
|
||
}
|