mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 03:38:04 +00:00
New CLI Flag --devel To Include Development/Prerelease Versions of Charts (#139)
* Include devel Flag for Toggling Dev Chart Versions The flag `--devel` for enabling/disabling dev versions of charts in following endpoints: 1. /api/helm/repositories/kafka-operator 2. /api/helm/repositories/versions 3. /api/helm/repositories/latestver Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io> * Run Tests on Devel Flag Related Changes Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io> --------- Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io>
This commit is contained in:
6
go.mod
6
go.mod
@@ -3,6 +3,7 @@ module github.com/komodorio/helm-dashboard
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.0
|
||||
github.com/eko/gocache/v3 v3.1.2
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
@@ -17,12 +18,14 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.4.0
|
||||
helm.sh/helm/v3 v3.11.1
|
||||
k8s.io/api v0.26.0
|
||||
k8s.io/apimachinery v0.26.0
|
||||
k8s.io/cli-runtime v0.26.0
|
||||
k8s.io/client-go v0.26.0
|
||||
k8s.io/kubectl v0.26.0
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -30,7 +33,6 @@ require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.3 // indirect
|
||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
||||
@@ -149,13 +151,11 @@ require (
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gotest.tools/v3 v3.4.0
|
||||
k8s.io/apiextensions-apiserver v0.26.0 // indirect
|
||||
k8s.io/apiserver v0.26.0 // indirect
|
||||
k8s.io/component-base v0.26.0 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
|
||||
oras.land/oras-go v1.2.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.12.1 // indirect
|
||||
|
||||
2
main.go
2
main.go
@@ -29,6 +29,7 @@ type options struct {
|
||||
BindHost string `long:"bind" description:"Host binding to start server (default: localhost)"` // default should be printed but not assigned as the precedence: flag > env > default
|
||||
Port uint `short:"p" long:"port" description:"Port to start server on" default:"8080"`
|
||||
Namespace string `short:"n" long:"namespace" description:"Namespace for HELM operations"`
|
||||
Devel bool `long:"devel" description:"Include development versions of charts"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -50,6 +51,7 @@ func main() {
|
||||
Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port),
|
||||
Debug: opts.Verbose,
|
||||
NoTracking: opts.NoTracking,
|
||||
Devel: opts.Devel,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/handlers"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
|
||||
@@ -13,14 +22,6 @@ import (
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var inMemStorage *storage.Storage
|
||||
@@ -111,7 +112,7 @@ func TestConfigureRoutes(t *testing.T) {
|
||||
|
||||
// Required arguements for route configuration
|
||||
abortWeb := func() {}
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -131,7 +132,7 @@ func TestContextSetter(t *testing.T) {
|
||||
con := GetTestGinContext(w)
|
||||
|
||||
// Required arguements
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -161,7 +162,7 @@ func TestNewRouter(t *testing.T) {
|
||||
|
||||
// Required arguemnets
|
||||
abortWeb := func() {}
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -183,7 +184,7 @@ func TestConfigureScanners(t *testing.T) {
|
||||
}
|
||||
|
||||
// Required arguemnets
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -206,7 +207,7 @@ func TestConfigureKubectls(t *testing.T) {
|
||||
}
|
||||
|
||||
// Required arguemnets
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -226,7 +227,7 @@ func TestConfigureKubectls(t *testing.T) {
|
||||
|
||||
func TestE2E(t *testing.T) {
|
||||
// Initialize data layer
|
||||
data, err := objects.NewDataLayer([]string{""}, "0.0.0-test", getFakeHelmConfig)
|
||||
data, err := objects.NewDataLayer([]string{""}, "0.0.0-test", getFakeHelmConfig, false)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Create a new router with the function
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/joomcode/errorx"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
|
||||
// Import to initialize client auth plugins.
|
||||
// From https://github.com/kubernetes/client-go/issues/242
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
@@ -22,7 +23,7 @@ type Application struct {
|
||||
Repositories *Repositories
|
||||
}
|
||||
|
||||
func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, namespaces []string) (*Application, error) {
|
||||
func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, namespaces []string, devel bool) (*Application, error) {
|
||||
hc, err := helmConfig(settings.Namespace())
|
||||
if err != nil {
|
||||
return nil, errorx.Decorate(err, "failed to get helm config for namespace '%s'", "")
|
||||
@@ -33,6 +34,11 @@ func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, na
|
||||
return nil, errorx.Decorate(err, "failed to get k8s client")
|
||||
}
|
||||
|
||||
semVerConstraint, err := versionConstaint(devel)
|
||||
if err != nil {
|
||||
return nil, errorx.Decorate(err, "failed to create semantic version constraint")
|
||||
}
|
||||
|
||||
return &Application{
|
||||
HelmConfig: helmConfig,
|
||||
K8s: k8s,
|
||||
@@ -44,6 +50,7 @@ func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, na
|
||||
Repositories: &Repositories{
|
||||
Settings: settings,
|
||||
HelmConfig: hc,
|
||||
versionConstraint: semVerConstraint,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/joomcode/errorx"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/pkg/errors"
|
||||
@@ -15,7 +17,6 @@ import (
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"io"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
@@ -30,6 +31,7 @@ type DataLayer struct {
|
||||
ConfGen HelmConfigGetter
|
||||
appPerContext map[string]*Application
|
||||
appPerContextMx *sync.Mutex
|
||||
devel bool
|
||||
}
|
||||
|
||||
type StatusInfo struct {
|
||||
@@ -40,7 +42,7 @@ type StatusInfo struct {
|
||||
ClusterMode bool
|
||||
}
|
||||
|
||||
func NewDataLayer(ns []string, ver string, cg HelmConfigGetter) (*DataLayer, error) {
|
||||
func NewDataLayer(ns []string, ver string, cg HelmConfigGetter, devel bool) (*DataLayer, error) {
|
||||
if cg == nil {
|
||||
return nil, errors.New("HelmConfigGetter can't be nil")
|
||||
}
|
||||
@@ -56,6 +58,7 @@ func NewDataLayer(ns []string, ver string, cg HelmConfigGetter) (*DataLayer, err
|
||||
ConfGen: cg,
|
||||
appPerContext: map[string]*Application{},
|
||||
appPerContextMx: new(sync.Mutex),
|
||||
devel: devel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ func (d *DataLayer) AppForCtx(ctx string) (*Application, error) {
|
||||
return d.ConfGen(settings, ns)
|
||||
}
|
||||
|
||||
a, err := NewApplication(settings, cfgGetter, d.Namespaces)
|
||||
a, err := NewApplication(settings, cfgGetter, d.Namespaces, d.devel)
|
||||
if err != nil {
|
||||
return nil, errorx.Decorate(err, "Failed to create application for context '%s'", ctx)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ func TestNewDataLayer(t *testing.T) {
|
||||
namespaces []string
|
||||
version string
|
||||
helmConfig HelmConfigGetter
|
||||
devel bool
|
||||
errorExpected bool
|
||||
}{
|
||||
{
|
||||
@@ -22,6 +23,7 @@ func TestNewDataLayer(t *testing.T) {
|
||||
namespaces: []string{"namespace1", "namespace2"},
|
||||
version: "1.0.0",
|
||||
helmConfig: nil,
|
||||
devel: false,
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
@@ -34,12 +36,13 @@ func TestNewDataLayer(t *testing.T) {
|
||||
helmConfig: func(sett *cli.EnvSettings, ns string) (*action.Configuration, error) {
|
||||
return &action.Configuration{}, nil
|
||||
},
|
||||
devel: false,
|
||||
errorExpected: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dl, err := NewDataLayer(tt.namespaces, tt.version, tt.helmConfig)
|
||||
dl, err := NewDataLayer(tt.namespaces, tt.version, tt.helmConfig, tt.devel)
|
||||
if tt.errorExpected {
|
||||
assert.Error(t, err, "Expected error but got nil")
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package objects
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/joomcode/errorx"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -11,10 +17,6 @@ import (
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const AnnRepo = "helm-dashboard/repository-name"
|
||||
@@ -23,6 +25,7 @@ type Repositories struct {
|
||||
Settings *cli.EnvSettings
|
||||
HelmConfig *action.Configuration
|
||||
mx sync.Mutex
|
||||
versionConstraint *semver.Constraints
|
||||
}
|
||||
|
||||
func (r *Repositories) Load() (*repo.File, error) {
|
||||
@@ -144,6 +147,7 @@ func (r *Repositories) Get(name string) (*Repository, error) {
|
||||
return &Repository{
|
||||
Settings: r.Settings,
|
||||
Orig: entry,
|
||||
versionConstraint: r.versionConstraint,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -166,6 +170,7 @@ func (r *Repositories) Containing(name string) (repo.ChartVersions, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
var updatedChartVersions repo.ChartVersions
|
||||
for _, v := range vers {
|
||||
// just using annotations here to attach a bit of information to the object
|
||||
// it has nothing to do with k8s annotations and should not get into manifests
|
||||
@@ -174,9 +179,22 @@ func (r *Repositories) Containing(name string) (repo.ChartVersions, error) {
|
||||
}
|
||||
|
||||
v.Annotations[AnnRepo] = rep.Orig.Name
|
||||
|
||||
// Validate the versions against semantic version constraints and filter
|
||||
version, err := semver.NewVersion(v.Version)
|
||||
if err != nil {
|
||||
// Ignored if version string is not parsable
|
||||
log.Debugf("failed to parse version string %q: %v", v.Version, err)
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, vers...) // TODO filter dev versions here, relates to #139
|
||||
if r.versionConstraint.Check(version) {
|
||||
// Add only versions that satisfy the semantic version constraint
|
||||
updatedChartVersions = append(updatedChartVersions, v)
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, updatedChartVersions...)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -220,6 +238,8 @@ type Repository struct {
|
||||
Settings *cli.EnvSettings
|
||||
Orig *repo.Entry
|
||||
mx sync.Mutex
|
||||
|
||||
versionConstraint *semver.Constraints
|
||||
}
|
||||
|
||||
func (r *Repository) indexFileName() string {
|
||||
@@ -247,9 +267,25 @@ func (r *Repository) Charts() ([]*repo.ChartVersion, error) {
|
||||
}
|
||||
|
||||
res := []*repo.ChartVersion{}
|
||||
for _, v := range ind.Entries {
|
||||
if len(v) > 0 { // TODO filter dev versions here, relates to #139
|
||||
res = append(res, v[0])
|
||||
for _, cv := range ind.Entries {
|
||||
for _, v := range cv {
|
||||
version, err := semver.NewVersion(v.Version)
|
||||
if err != nil {
|
||||
// Ignored if version string is not parsable
|
||||
log.Debugf("failed to parse version string %q: %v", v.Version, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if r.versionConstraint.Check(version) {
|
||||
// Add only versions that satisfy the semantic version constraint
|
||||
res = append(res, v)
|
||||
|
||||
// Only the highest version satisfying the constraint is required. Hence, break.
|
||||
// The constraint here is (only stable versions) vs (stable + dev/prerelease).
|
||||
// If dev versions are disabled and chart only has dev versions,
|
||||
// chart is excluded from the result.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,3 +346,23 @@ func removeRepoCache(root, name string) error {
|
||||
}
|
||||
return os.Remove(idx)
|
||||
}
|
||||
|
||||
// versionConstaint returns semantic version constraint instance that can be used to
|
||||
// validate the version of repositories. The flag isDevelEnabled is used to configure
|
||||
// enabling/disabling of development/prerelease versions of charts.
|
||||
func versionConstaint(isDevelEnabled bool) (*semver.Constraints, error) {
|
||||
// When devel flag is disabled. i.e., Only stable releases are included.
|
||||
version := ">0.0.0"
|
||||
|
||||
if isDevelEnabled {
|
||||
// When devel flag is enabled. i.e., Prereleases (alpha, beta, release candidate, etc.) are included.
|
||||
version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
constraint, err := semver.NewConstraint(version)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid version constraint format %q", version)
|
||||
}
|
||||
|
||||
return constraint, nil
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
package objects
|
||||
|
||||
import (
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
)
|
||||
|
||||
var filePath = "./testdata/repositories.yaml"
|
||||
const (
|
||||
validRepositoryConfigPath = "./testdata/repositories.yaml"
|
||||
invalidCacheFileRepositoryConfigPath = "./testdata/repositories-invalid-cache-file.yaml"
|
||||
invalidMalformedManifestRepositoryConfigPath = "./testdata/repositories-malformed-manifest.yaml"
|
||||
)
|
||||
|
||||
func initRepository(t *testing.T) *Repositories {
|
||||
func initRepository(t *testing.T, filePath string, devel bool) *Repositories {
|
||||
t.Helper()
|
||||
|
||||
settings := cli.New()
|
||||
@@ -40,6 +44,11 @@ func initRepository(t *testing.T) *Repositories {
|
||||
}
|
||||
})
|
||||
|
||||
vc, err := versionConstaint(devel)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Sets the repository file path
|
||||
settings.RepositoryConfig = fname.Name()
|
||||
settings.RepositoryCache = path.Dir(filePath)
|
||||
@@ -47,13 +56,14 @@ func initRepository(t *testing.T) *Repositories {
|
||||
testRepository := &Repositories{
|
||||
Settings: settings,
|
||||
HelmConfig: &action.Configuration{}, // maybe use copy of getFakeHelmConfig from api_test.go
|
||||
versionConstraint: vc,
|
||||
}
|
||||
|
||||
return testRepository
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
testRepository := initRepository(t)
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
repos, err := testRepository.List()
|
||||
if err != nil {
|
||||
@@ -67,7 +77,7 @@ func TestAdd(t *testing.T) {
|
||||
testRepoName := "TEST"
|
||||
testRepoUrl := "https://helm.github.io/examples"
|
||||
|
||||
testRepository := initRepository(t)
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
err := testRepository.Add(testRepoName, testRepoUrl)
|
||||
if err != nil {
|
||||
t.Fatal(err, "Failed to add repo")
|
||||
@@ -82,7 +92,7 @@ func TestAdd(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
testRepository := initRepository(t)
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
testRepoName := "charts" // don't ever delete 'testing'!
|
||||
err := testRepository.Delete(testRepoName)
|
||||
@@ -100,7 +110,7 @@ func TestGet(t *testing.T) {
|
||||
// Initial repositiry name in test file
|
||||
repoName := "charts"
|
||||
|
||||
testRepository := initRepository(t)
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
repo, err := testRepository.Get(repoName)
|
||||
if err != nil {
|
||||
@@ -110,8 +120,8 @@ func TestGet(t *testing.T) {
|
||||
assert.Equal(t, repo.Orig.Name, repoName)
|
||||
}
|
||||
|
||||
func TestCharts(t *testing.T) {
|
||||
testRepository := initRepository(t)
|
||||
func TestRepository_Charts_DevelDisabled(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
r, err := testRepository.Get("testing")
|
||||
if err != nil {
|
||||
@@ -123,7 +133,164 @@ func TestCharts(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(charts) != 2 {
|
||||
t.Fatalf("Wrong charts len: %d", len(charts))
|
||||
// Total charts in ./testdata/testing-index.yaml = 4
|
||||
// Excluded charts = 2 (1 has invalid version, 1 has only dev version)
|
||||
// Included charts = 2 (2 stable versions)
|
||||
expectedCount := 2
|
||||
if len(charts) != expectedCount {
|
||||
t.Fatalf("Wrong charts count: %d, expected: %d", len(charts), expectedCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_Charts_DevelEnabled(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, true)
|
||||
|
||||
r, err := testRepository.Get("testing")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
charts, err := r.Charts()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total charts in ./testdata/testing-index.yaml = 4
|
||||
// Excluded charts = 1 (1 has invalid version)
|
||||
// Included charts = 3 (2 stable versions, 1 has only dev version)
|
||||
expectedCount := 3
|
||||
if len(charts) != expectedCount {
|
||||
t.Fatalf("Wrong charts count: %d, expected: %d", len(charts), expectedCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_Charts_InvalidCacheFile(t *testing.T) {
|
||||
testRepository := initRepository(t, invalidCacheFileRepositoryConfigPath, false)
|
||||
|
||||
r, err := testRepository.Get("non-existing")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = r.Charts()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for invalid cache file path, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelDisable(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
chartVersions, err := testRepository.Containing("alpine")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart alpine in ./testdata/testing-index.yaml = 3
|
||||
// Excluded charts = 1 (1 dev version)
|
||||
// Included charts = 2 (2 stable versions)
|
||||
expectedCount := 2
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelEnabled(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, true)
|
||||
|
||||
chartVersions, err := testRepository.Containing("alpine")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart alpine in ./testdata/testing-index.yaml = 3
|
||||
// Excluded charts = 0
|
||||
// Included charts = 3 (2 stable versions, 1 dev version)
|
||||
expectedCount := 3
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelDisable_OnlyDevVersionsOfChartAvailable(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
chartVersions, err := testRepository.Containing("traefik")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart traefik in ./testdata/testing-index.yaml = 1
|
||||
// Excluded charts = 1 (1 dev version)
|
||||
// Included charts = 0
|
||||
expectedCount := 0
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelEnabled_OnlyDevVersionsOfChartAvailable(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, true)
|
||||
|
||||
chartVersions, err := testRepository.Containing("traefik")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart traefik in ./testdata/testing-index.yaml = 1
|
||||
// Excluded charts = 0
|
||||
// Included charts = 1 (1 dev version)
|
||||
expectedCount := 1
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelDisable_InvalidChartVersion(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, false)
|
||||
|
||||
chartVersions, err := testRepository.Containing("rabbitmq")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart rabbitmq in ./testdata/testing-index.yaml = 1
|
||||
// Excluded charts = 1 (1 invalid version)
|
||||
// Included charts = 0
|
||||
expectedCount := 0
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_DevelEnabled_InvalidChartVersion(t *testing.T) {
|
||||
testRepository := initRepository(t, validRepositoryConfigPath, true)
|
||||
|
||||
chartVersions, err := testRepository.Containing("rabbitmq")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Total versions of chart rabbitmq in ./testdata/testing-index.yaml = 1
|
||||
// Excluded charts = 1 (1 invalid version)
|
||||
// Included charts = 0
|
||||
expectedCount := 0
|
||||
if len(chartVersions) != expectedCount {
|
||||
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepositories_Containing_MalformedRepositoryConfigFile(t *testing.T) {
|
||||
testRepository := initRepository(t, invalidMalformedManifestRepositoryConfigPath, false)
|
||||
|
||||
_, err := testRepository.Containing("alpine")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for malformed RepositoryConfig file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
6
pkg/dashboard/objects/testdata/repositories-invalid-cache-file.yaml
vendored
Normal file
6
pkg/dashboard/objects/testdata/repositories-invalid-cache-file.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: ""
|
||||
generated: "0001-01-01T00:00:00Z"
|
||||
repositories:
|
||||
- cache: non-existing-index.yaml
|
||||
name: non-existing
|
||||
url: http://example.com/charts
|
||||
12
pkg/dashboard/objects/testdata/repositories-malformed-manifest.yaml
vendored
Normal file
12
pkg/dashboard/objects/testdata/repositories-malformed-manifest.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: ""
|
||||
generated: "0001-01-01T00:00:00Z"
|
||||
- repositories:
|
||||
- caFile: ""
|
||||
certFile: ""
|
||||
insecure_skip_tls_verify: false
|
||||
keyFile: ""
|
||||
name: charts
|
||||
pass_credentials_all: false
|
||||
password: ""
|
||||
url: https://charts.helm.sh/stable
|
||||
username: ""
|
||||
@@ -64,3 +64,37 @@ entries:
|
||||
email: containers@bitnami.com
|
||||
icon: ""
|
||||
apiVersion: v2
|
||||
traefik:
|
||||
- apiVersion: v1
|
||||
appVersion: 1.7.26
|
||||
deprecated: true
|
||||
description: A Traefik based Kubernetes ingress controller with Let's
|
||||
Encrypt support
|
||||
home: https://traefik.io/
|
||||
icon: https://docs.traefik.io/assets/img/traefik.logo.png
|
||||
keywords:
|
||||
- traefik
|
||||
- ingress
|
||||
- acme
|
||||
- letsencrypt
|
||||
name: traefik
|
||||
sources:
|
||||
- https://github.com/containous/traefik
|
||||
- https://github.com/helm/charts/tree/master/stable/traefik
|
||||
version: 1.87.7-rc1
|
||||
rabbitmq:
|
||||
- apiVersion: v1
|
||||
appVersion: 3.8.2
|
||||
deprecated: true
|
||||
description: DEPRECATED Open source message broker software that implements the Advanced
|
||||
Message Queuing Protocol (AMQP)
|
||||
home: https://www.rabbitmq.com
|
||||
icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png
|
||||
keywords:
|
||||
- rabbitmq
|
||||
- message queue
|
||||
- AMQP
|
||||
name: rabbitmq
|
||||
sources:
|
||||
- https://github.com/bitnami/bitnami-docker-rabbitmq
|
||||
version: invalid-version
|
||||
|
||||
@@ -4,16 +4,17 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joomcode/errorx"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hashicorp/go-version"
|
||||
@@ -28,10 +29,11 @@ type Server struct {
|
||||
Address string
|
||||
Debug bool
|
||||
NoTracking bool
|
||||
Devel bool
|
||||
}
|
||||
|
||||
func (s *Server) StartServer(ctx context.Context, cancel context.CancelFunc) (string, utils.ControlChan, error) {
|
||||
data, err := objects.NewDataLayer(s.Namespaces, s.Version, NewHelmConfig)
|
||||
data, err := objects.NewDataLayer(s.Namespaces, s.Version, NewHelmConfig, s.Devel)
|
||||
if err != nil {
|
||||
return "", nil, errorx.Decorate(err, "Failed to create data layer")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user