diff --git a/go.mod b/go.mod index a43b942..530c9da 100644 --- a/go.mod +++ b/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 diff --git a/main.go b/main.go index 175c303..8c4cafc 100644 --- a/main.go +++ b/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()) diff --git a/pkg/dashboard/api_test.go b/pkg/dashboard/api_test.go index e2cb3de..86b3586 100644 --- a/pkg/dashboard/api_test.go +++ b/pkg/dashboard/api_test.go @@ -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 diff --git a/pkg/dashboard/objects/app.go b/pkg/dashboard/objects/app.go index edf3e2d..f5039f9 100644 --- a/pkg/dashboard/objects/app.go +++ b/pkg/dashboard/objects/app.go @@ -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, @@ -42,8 +48,9 @@ func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, na HelmConfig: helmConfig, }, Repositories: &Repositories{ - Settings: settings, - HelmConfig: hc, + Settings: settings, + HelmConfig: hc, + versionConstraint: semVerConstraint, }, }, nil } diff --git a/pkg/dashboard/objects/data.go b/pkg/dashboard/objects/data.go index 8f19cbf..f5883c0 100644 --- a/pkg/dashboard/objects/data.go +++ b/pkg/dashboard/objects/data.go @@ -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) } diff --git a/pkg/dashboard/objects/data_test.go b/pkg/dashboard/objects/data_test.go index 868534d..5fda9f7 100644 --- a/pkg/dashboard/objects/data_test.go +++ b/pkg/dashboard/objects/data_test.go @@ -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 { diff --git a/pkg/dashboard/objects/repos.go b/pkg/dashboard/objects/repos.go index f25394e..5aedad3 100644 --- a/pkg/dashboard/objects/repos.go +++ b/pkg/dashboard/objects/repos.go @@ -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,18 +17,15 @@ 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" type Repositories struct { - Settings *cli.EnvSettings - HelmConfig *action.Configuration - mx sync.Mutex + Settings *cli.EnvSettings + HelmConfig *action.Configuration + mx sync.Mutex + versionConstraint *semver.Constraints } func (r *Repositories) Load() (*repo.File, error) { @@ -142,8 +145,9 @@ func (r *Repositories) Get(name string) (*Repository, error) { for _, entry := range f.Repositories { if entry.Name == name { return &Repository{ - Settings: r.Settings, - Orig: entry, + 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 + } + + if r.versionConstraint.Check(version) { + // Add only versions that satisfy the semantic version constraint + updatedChartVersions = append(updatedChartVersions, v) + } } - res = append(res, vers...) // TODO filter dev versions here, relates to #139 + 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 +} diff --git a/pkg/dashboard/objects/repos_test.go b/pkg/dashboard/objects/repos_test.go index ceb517f..f9c2b50 100644 --- a/pkg/dashboard/objects/repos_test.go +++ b/pkg/dashboard/objects/repos_test.go @@ -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,20 +44,26 @@ 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) testRepository := &Repositories{ - Settings: settings, - HelmConfig: &action.Configuration{}, // maybe use copy of getFakeHelmConfig from api_test.go + 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") } } diff --git a/pkg/dashboard/objects/testdata/repositories-invalid-cache-file.yaml b/pkg/dashboard/objects/testdata/repositories-invalid-cache-file.yaml new file mode 100644 index 0000000..d3fb33d --- /dev/null +++ b/pkg/dashboard/objects/testdata/repositories-invalid-cache-file.yaml @@ -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 diff --git a/pkg/dashboard/objects/testdata/repositories-malformed-manifest.yaml b/pkg/dashboard/objects/testdata/repositories-malformed-manifest.yaml new file mode 100644 index 0000000..56b126b --- /dev/null +++ b/pkg/dashboard/objects/testdata/repositories-malformed-manifest.yaml @@ -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: "" diff --git a/pkg/dashboard/objects/testdata/testing-index.yaml b/pkg/dashboard/objects/testdata/testing-index.yaml index 91e4d46..652b9b0 100644 --- a/pkg/dashboard/objects/testdata/testing-index.yaml +++ b/pkg/dashboard/objects/testdata/testing-index.yaml @@ -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 diff --git a/pkg/dashboard/server.go b/pkg/dashboard/server.go index 2c8e8b1..a668f57 100644 --- a/pkg/dashboard/server.go +++ b/pkg/dashboard/server.go @@ -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") }