diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_integration_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_integration_test.go new file mode 100644 index 00000000000..ce9fe3f93b5 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_integration_test.go @@ -0,0 +1,241 @@ +// Copyright 2021-2022 the Kubeapps contributors. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + corev1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1" + "golang.org/x/sync/semaphore" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/apimachinery/pkg/util/sets" +) + +// This is an integration test: it tests the full integration of flux plugin with flux back-end +// To run these tests, enable ENABLE_FLUX_INTEGRATION_TESTS variable +// pre-requisites for these tests to run: +// 1) kind cluster with flux deployed +// 2) kubeapps apis apiserver service running with fluxv2 plug-in enabled, port forwarded to 8080, e.g. +// kubectl -n kubeapps port-forward svc/kubeapps-internal-kubeappsapis 8080:8080 +// 3) run './kind-cluster-setup.sh deploy' once prior to these tests + +// this integration test is meant to test a scenario when the redis cache is confiured with maxmemory +// too small to be able to fit all the repos needed to satisfy the request for GetAvailablePackageSummaries +// and redis cache eviction kicks in. Also, the kubeapps-apis pod should have a large memory limit (1Gb) set +// To set up such environment one can use "-f ./docs/user/manifests/kubeapps-local-dev-redis-tiny-values.yaml" +// option when installing kubeapps via "helm upgrade" +// It is worth noting that exactly how many copies of bitnami repo can be held in the cache at any given time varies +// This is because the size of the index.yaml we get from bitnami does fluctuate quite a bit over time: +// [kubeapps]$ ls -l bitnami_index.yaml +// -rw-r--r--@ 1 gfichtenholt staff 8432962 Jun 20 02:35 bitnami_index.yaml +// [kubeapps]$ ls -l bitnami_index.yaml +// -rw-rw-rw-@ 1 gfichtenholt staff 10394218 Nov 7 19:41 bitnami_index.yaml +// Also now we are caching helmcharts themselves for each repo so that will affect how many will fit too +func TestKindClusterGetAvailablePackageSummariesForLargeReposAndTinyRedis(t *testing.T) { + fluxPlugin, _ := checkEnv(t) + + redisCli, err := newRedisClientForIntegrationTest(t) + if err != nil { + t.Fatalf("%+v", err) + } + + // assume 30Mb redis cache for now. See comment above + if err = redisCheckTinyMaxMemory(t, redisCli, "31457280"); err != nil { + t.Fatalf("%v", err) + } + + // ref https://redis.io/topics/notifications + if err = redisCli.ConfigSet(redisCli.Context(), "notify-keyspace-events", "EA").Err(); err != nil { + t.Fatalf("%+v", err) + } + t.Cleanup(func() { + t.Logf("Resetting notify-keyspace-events") + if err = redisCli.ConfigSet(redisCli.Context(), "notify-keyspace-events", "").Err(); err != nil { + t.Logf("%v", err) + } + }) + + if err = initNumberOfChartsInBitnamiCatalog(t); err != nil { + t.Errorf("Failed to get number of charts in bitnami catalog due to: %v", err) + } + + const MAX_REPOS_NEVER = 100 + var totalRepos = 0 + // ref https://stackoverflow.com/questions/32840687/timeout-for-waitgroup-wait + evictedRepos := sets.String{} + + // do this part in a func so we can defer subscribe.Close + func() { + // ref https://medium.com/nerd-for-tech/redis-getting-notified-when-a-key-is-expired-or-changed-ca3e1f1c7f0a + subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") + defer subscribe.Close() + + sem := semaphore.NewWeighted(MAX_REPOS_NEVER) + if err := sem.Acquire(context.Background(), MAX_REPOS_NEVER); err != nil { + t.Fatalf("%v", err) + } + + go redisReceiveNotificationsLoop(t, subscribe.Channel(), sem, &evictedRepos) + + // now load some large repos (bitnami) + // I didn't want to store a large (>10MB) copy of bitnami repo in our git, + // so for now let it fetch directly from bitnami website + // we'll keep adding repos one at a time, until we get an event from redis + // about the first evicted repo entry + for ; totalRepos < MAX_REPOS_NEVER && evictedRepos.Len() == 0; totalRepos++ { + repo := fmt.Sprintf("bitnami-%d", totalRepos) + // this is to make sure we allow enough time for repository to be created and come to ready state + if err = kubeAddHelmRepository(t, repo, "https://charts.bitnami.com/bitnami", "default", ""); err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + if err = kubeDeleteHelmRepository(t, repo, "default"); err != nil { + t.Logf("%v", err) + } + }) + // wait until this repo have been indexed and cached up to 10 minutes + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) + defer cancel() + if err := sem.Acquire(ctx, 1); err != nil { + t.Fatalf("Timed out waiting for Redis event: %v", err) + } + } + t.Logf("Done with first part of the test, total repos: [%d], evicted repos: [%d]", + totalRepos, len(evictedRepos)) + }() + + if evictedRepos.Len() == 0 { + t.Fatalf("Failing because redis did not evict any entries") + } + + if keys, err := redisCli.Keys(redisCli.Context(), "helmrepositories:*").Result(); err != nil { + t.Fatalf("%v", err) + } else { + // the cache should only big enough to be able to hold at most (totalRepos-1) of the keys + // one (or more) entries may have been evicted + if len(keys) > totalRepos-1 { + t.Fatalf("Expected at most [%d] keys in cache but got: %s", totalRepos-1, keys) + } + } + + // one particular code path I'd like to test: + // make sure that GetAvailablePackageVersions() works w.r.t. a cache entry that's been evicted + grpcContext := newGrpcAdminContext(t, "test-create-admin") + + // copy the evicted list because before ForEach loop below will modify it in a goroutine + evictedCopy := sets.StringKeySet(evictedRepos) + + // do this part in a func so we can defer subscribe.Close + func() { + subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") + defer subscribe.Close() + + go redisReceiveNotificationsLoop(t, subscribe.Channel(), nil, &evictedRepos) + + for _, k := range evictedCopy.List() { + name := strings.Split(k, ":")[2] + t.Logf("Checking apache version in repo [%s]...", name) + grpcContext, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) + defer cancel() + resp, err := fluxPlugin.GetAvailablePackageVersions( + grpcContext, &corev1.GetAvailablePackageVersionsRequest{ + AvailablePackageRef: &corev1.AvailablePackageReference{ + Context: &corev1.Context{ + Namespace: "default", + }, + Identifier: name + "/apache", + }, + }) + if err != nil { + t.Fatalf("%v", err) + } else if len(resp.PackageAppVersions) < 5 { + t.Fatalf("Expected at least 5 versions for apache chart, got: %s", resp) + } + } + + t.Logf("Done with second part of the test") + }() + + // do this part in a func so we can defer subscribe.Close + func() { + subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") + defer subscribe.Close() + + // above loop should cause a few more entries to be evicted, but just to be sure let's + // load a few more copies of bitnami repo into the cache. The goal of this for loop is + // to force redis to evict more repo(s) + sem := semaphore.NewWeighted(MAX_REPOS_NEVER) + if err := sem.Acquire(context.Background(), MAX_REPOS_NEVER); err != nil { + t.Fatalf("%v", err) + } + go redisReceiveNotificationsLoop(t, subscribe.Channel(), sem, &evictedRepos) + + for ; totalRepos < MAX_REPOS_NEVER && evictedRepos.Len() == evictedCopy.Len(); totalRepos++ { + repo := fmt.Sprintf("bitnami-%d", totalRepos) + // this is to make sure we allow enough time for repository to be created and come to ready state + if err = kubeAddHelmRepository(t, repo, "https://charts.bitnami.com/bitnami", "default", ""); err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + if err = kubeDeleteHelmRepository(t, repo, "default"); err != nil { + t.Logf("%v", err) + } + }) + // wait until this repo have been indexed and cached up to 10 minutes + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) + defer cancel() + if err := sem.Acquire(ctx, 1); err != nil { + t.Fatalf("Timed out waiting for Redis event: %v", err) + } + } + + t.Logf("Done with third part of the test") + }() + + if keys, err := redisCli.Keys(redisCli.Context(), "helmrepositories:*").Result(); err != nil { + t.Fatalf("%v", err) + } else { + // the cache should only big enough to be able to hold at most (totalRepos-1) of the keys + // one (or more) entries MUST have been evicted + if len(keys) > totalRepos-1 { + t.Fatalf("Expected at most %d keys in cache but got [%s]", totalRepos-1, keys) + } + } + + // not related to low maxmemory but as long as we are here might as well check that + // there is a Unauthenticated failure when there are no credenitals in the request + _, err = fluxPlugin.GetAvailablePackageSummaries(context.TODO(), &corev1.GetAvailablePackageSummariesRequest{}) + if err == nil || status.Code(err) != codes.Unauthenticated { + t.Fatalf("Expected Unauthenticated, got %v", err) + } + + grpcContext, cancel := context.WithTimeout(grpcContext, 60*time.Second) + defer cancel() + resp2, err := fluxPlugin.GetAvailablePackageSummaries(grpcContext, &corev1.GetAvailablePackageSummariesRequest{}) + if err != nil { + t.Fatalf("%v", err) + } + + // we need to make sure that response contains packages from all existing repositories + // regardless whether they're in the cache or not + expected := sets.String{} + for i := 0; i < totalRepos; i++ { + repo := fmt.Sprintf("bitnami-%d", i) + expected.Insert(repo) + } + for _, s := range resp2.AvailablePackageSummaries { + id := strings.Split(s.AvailablePackageRef.Identifier, "/") + expected.Delete(id[0]) + } + + if expected.Len() != 0 { + t.Fatalf("Expected to get packages from these repositories: %s, but did not get any", + expected.List()) + } +} diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_test.go index ea3c47e5bb3..d4408a0f5e6 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/chart_test.go @@ -95,15 +95,7 @@ func TestGetAvailablePackageDetail(t *testing.T) { // these will be used further on for TLS-related scenarios. Init // byte arrays up front so they can be re-used in multiple places later - var ca, pub, priv []byte - var err error - if ca, err = ioutil.ReadFile("testdata/rootCA.crt"); err != nil { - t.Fatalf("%+v", err) - } else if pub, err = ioutil.ReadFile("testdata/crt.pem"); err != nil { - t.Fatalf("%+v", err) - } else if priv, err = ioutil.ReadFile("testdata/key.pem"); err != nil { - t.Fatalf("%+v", err) - } + ca, pub, priv := getCertsForTesting(t) for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { @@ -140,12 +132,6 @@ func TestGetAvailablePackageDetail(t *testing.T) { } var ts *httptest.Server if tc.tls { - // I cheated a bit in this test. Instead of generating my own certificates - // and keys using openssl tool, which I found time consuming and overly complicated, - // I just copied the ones being used by helm.sh tool for testing purposes - // from https://github.com/helm/helm/tree/main/testdata - // in order to save some time. Should n't affect any functionality of productionn - // code ts = httptest.NewUnstartedServer(handler) tlsConf, err := httpclient.NewClientTLS(pub, priv, ca) if err != nil { @@ -694,7 +680,7 @@ func TestChartCacheResyncNotIdle(t *testing.T) { } // what I need is a single repo with a whole bunch of unique charts (packages) - tarGzBytes, err := ioutil.ReadFile("./testdata/redis-14.4.0.tgz") + tarGzBytes, err := ioutil.ReadFile("./testdata/charts/redis-14.4.0.tgz") if err != nil { t.Fatalf("%+v", err) } @@ -733,7 +719,7 @@ func TestChartCacheResyncNotIdle(t *testing.T) { repoName := "multitude-of-charts" repoNamespace := "default" replaceUrls := make(map[string]string) - replaceUrls["{{testdata/redis-14.4.0.tgz}}"] = ts.URL + replaceUrls["{{testdata/charts/redis-14.4.0.tgz}}"] = ts.URL ts2, r, err := newRepoWithIndex( tmpFile.Name(), repoName, repoNamespace, replaceUrls, "") if err != nil { @@ -977,12 +963,12 @@ func compareActualVsExpectedAvailablePackageDetail(t *testing.T, actual *corev1. var redis_charts_spec = []testSpecChartWithFile{ { name: "redis", - tgzFile: "testdata/redis-14.4.0.tgz", + tgzFile: "testdata/charts/redis-14.4.0.tgz", revision: "14.4.0", }, { name: "redis", - tgzFile: "testdata/redis-14.3.4.tgz", + tgzFile: "testdata/charts/redis-14.3.4.tgz", revision: "14.3.4", }, } diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/integration_utils_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/integration_utils_test.go index 2c444f06a7f..90a5960423f 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/integration_utils_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/integration_utils_test.go @@ -51,6 +51,19 @@ const ( // EnvvarFluxIntegrationTests enables tests that run against a local kind cluster envVarFluxIntegrationTests = "ENABLE_FLUX_INTEGRATION_TESTS" defaultContextTimeout = 30 * time.Second + + // This is local copy of the first few entries + // on "https://stefanprodan.github.io/podinfo/index.yaml" as of Sept 10 2021 with the chart + // urls modified to link to .tgz files also within the local cluster. + // If we want other repos, we'll have add directories and tinker with ./Dockerfile and NGINX conf. + // This relies on fluxv2plugin-testdata-svc service stood up by testdata/kind-cluster-setup.sh + podinfo_repo_url = "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo" + + // same as above but requires HTTP basic authentication: user: foo, password: bar + podinfo_basic_auth_repo_url = "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo-basic-auth" + + // same as above but requires TLS + podinfo_tls_repo_url = "https://fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local:443" ) func checkEnv(t *testing.T) (fluxplugin.FluxV2PackagesServiceClient, fluxplugin.FluxV2RepositoriesServiceClient) { diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_integration_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_integration_test.go index d6aa9138578..cb206c809cc 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_integration_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_integration_test.go @@ -5,7 +5,6 @@ package main import ( "context" - "fmt" "strings" "testing" "time" @@ -15,11 +14,8 @@ import ( corev1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1" plugins "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/plugins/v1alpha1" fluxplugin "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/plugins/fluxv2/packages/v1alpha1" - "golang.org/x/sync/semaphore" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - apiv1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/sets" ) // This is an integration test: it tests the full integration of flux plugin with flux back-end @@ -30,18 +26,6 @@ import ( // kubectl -n kubeapps port-forward svc/kubeapps-internal-kubeappsapis 8080:8080 // 3) run './kind-cluster-setup.sh deploy' once prior to these tests -const ( - // This is local copy of the first few entries - // on "https://stefanprodan.github.io/podinfo/index.yaml" as of Sept 10 2021 with the chart - // urls modified to link to .tgz files also within the local cluster. - // If we want other repos, we'll have add directories and tinker with ./Dockerfile and NGINX conf. - // This relies on fluxv2plugin-testdata-svc service stood up by testdata/kind-cluster-setup.sh - podinfo_repo_url = "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo" - - // same as above but requires HTTP basic authentication: user: foo, password: bar - podinfo_basic_auth_repo_url = "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo-basic-auth" -) - type integrationTestCreatePackageSpec struct { testName string repoUrl string @@ -408,531 +392,6 @@ func TestKindClusterDeleteInstalledPackage(t *testing.T) { } } -// this integration test is meant to test a scenario when the redis cache is confiured with maxmemory -// too small to be able to fit all the repos needed to satisfy the request for GetAvailablePackageSummaries -// and redis cache eviction kicks in. Also, the kubeapps-apis pod should have a large memory limit (1Gb) set -// To set up such environment one can use "-f ./docs/user/manifests/kubeapps-local-dev-redis-tiny-values.yaml" -// option when installing kubeapps via "helm upgrade" -// It is worth noting that exactly how many copies of bitnami repo can be held in the cache at any given time varies -// This is because the size of the index.yaml we get from bitnami does fluctuate quite a bit over time: -// [kubeapps]$ ls -l bitnami_index.yaml -// -rw-r--r--@ 1 gfichtenholt staff 8432962 Jun 20 02:35 bitnami_index.yaml -// [kubeapps]$ ls -l bitnami_index.yaml -// -rw-rw-rw-@ 1 gfichtenholt staff 10394218 Nov 7 19:41 bitnami_index.yaml -// Also now we are caching helmcharts themselves for each repo so that will affect how many will fit too -func TestKindClusterGetAvailablePackageSummariesForLargeReposAndTinyRedis(t *testing.T) { - fluxPlugin, _ := checkEnv(t) - - redisCli, err := newRedisClientForIntegrationTest(t) - if err != nil { - t.Fatalf("%+v", err) - } - - // assume 30Mb redis cache for now. See comment above - if err = redisCheckTinyMaxMemory(t, redisCli, "31457280"); err != nil { - t.Fatalf("%v", err) - } - - // ref https://redis.io/topics/notifications - if err = redisCli.ConfigSet(redisCli.Context(), "notify-keyspace-events", "EA").Err(); err != nil { - t.Fatalf("%+v", err) - } - t.Cleanup(func() { - t.Logf("Resetting notify-keyspace-events") - if err = redisCli.ConfigSet(redisCli.Context(), "notify-keyspace-events", "").Err(); err != nil { - t.Logf("%v", err) - } - }) - - if err = initNumberOfChartsInBitnamiCatalog(t); err != nil { - t.Errorf("Failed to get number of charts in bitnami catalog due to: %v", err) - } - - const MAX_REPOS_NEVER = 100 - var totalRepos = 0 - // ref https://stackoverflow.com/questions/32840687/timeout-for-waitgroup-wait - evictedRepos := sets.String{} - - // do this part in a func so we can defer subscribe.Close - func() { - // ref https://medium.com/nerd-for-tech/redis-getting-notified-when-a-key-is-expired-or-changed-ca3e1f1c7f0a - subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") - defer subscribe.Close() - - sem := semaphore.NewWeighted(MAX_REPOS_NEVER) - if err := sem.Acquire(context.Background(), MAX_REPOS_NEVER); err != nil { - t.Fatalf("%v", err) - } - - go redisReceiveNotificationsLoop(t, subscribe.Channel(), sem, &evictedRepos) - - // now load some large repos (bitnami) - // I didn't want to store a large (>10MB) copy of bitnami repo in our git, - // so for now let it fetch directly from bitnami website - // we'll keep adding repos one at a time, until we get an event from redis - // about the first evicted repo entry - for ; totalRepos < MAX_REPOS_NEVER && evictedRepos.Len() == 0; totalRepos++ { - repo := fmt.Sprintf("bitnami-%d", totalRepos) - // this is to make sure we allow enough time for repository to be created and come to ready state - if err = kubeAddHelmRepository(t, repo, "https://charts.bitnami.com/bitnami", "default", ""); err != nil { - t.Fatalf("%v", err) - } - t.Cleanup(func() { - if err = kubeDeleteHelmRepository(t, repo, "default"); err != nil { - t.Logf("%v", err) - } - }) - // wait until this repo have been indexed and cached up to 10 minutes - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) - defer cancel() - if err := sem.Acquire(ctx, 1); err != nil { - t.Fatalf("Timed out waiting for Redis event: %v", err) - } - } - t.Logf("Done with first part of the test, total repos: [%d], evicted repos: [%d]", - totalRepos, len(evictedRepos)) - }() - - if evictedRepos.Len() == 0 { - t.Fatalf("Failing because redis did not evict any entries") - } - - if keys, err := redisCli.Keys(redisCli.Context(), "helmrepositories:*").Result(); err != nil { - t.Fatalf("%v", err) - } else { - // the cache should only big enough to be able to hold at most (totalRepos-1) of the keys - // one (or more) entries may have been evicted - if len(keys) > totalRepos-1 { - t.Fatalf("Expected at most [%d] keys in cache but got: %s", totalRepos-1, keys) - } - } - - // one particular code path I'd like to test: - // make sure that GetAvailablePackageVersions() works w.r.t. a cache entry that's been evicted - grpcContext := newGrpcAdminContext(t, "test-create-admin") - - // copy the evicted list because before ForEach loop below will modify it in a goroutine - evictedCopy := sets.StringKeySet(evictedRepos) - - // do this part in a func so we can defer subscribe.Close - func() { - subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") - defer subscribe.Close() - - go redisReceiveNotificationsLoop(t, subscribe.Channel(), nil, &evictedRepos) - - for _, k := range evictedCopy.List() { - name := strings.Split(k, ":")[2] - t.Logf("Checking apache version in repo [%s]...", name) - grpcContext, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) - defer cancel() - resp, err := fluxPlugin.GetAvailablePackageVersions( - grpcContext, &corev1.GetAvailablePackageVersionsRequest{ - AvailablePackageRef: &corev1.AvailablePackageReference{ - Context: &corev1.Context{ - Namespace: "default", - }, - Identifier: name + "/apache", - }, - }) - if err != nil { - t.Fatalf("%v", err) - } else if len(resp.PackageAppVersions) < 5 { - t.Fatalf("Expected at least 5 versions for apache chart, got: %s", resp) - } - } - - t.Logf("Done with second part of the test") - }() - - // do this part in a func so we can defer subscribe.Close - func() { - subscribe := redisCli.PSubscribe(redisCli.Context(), "__keyevent@0__:*") - defer subscribe.Close() - - // above loop should cause a few more entries to be evicted, but just to be sure let's - // load a few more copies of bitnami repo into the cache. The goal of this for loop is - // to force redis to evict more repo(s) - sem := semaphore.NewWeighted(MAX_REPOS_NEVER) - if err := sem.Acquire(context.Background(), MAX_REPOS_NEVER); err != nil { - t.Fatalf("%v", err) - } - go redisReceiveNotificationsLoop(t, subscribe.Channel(), sem, &evictedRepos) - - for ; totalRepos < MAX_REPOS_NEVER && evictedRepos.Len() == evictedCopy.Len(); totalRepos++ { - repo := fmt.Sprintf("bitnami-%d", totalRepos) - // this is to make sure we allow enough time for repository to be created and come to ready state - if err = kubeAddHelmRepository(t, repo, "https://charts.bitnami.com/bitnami", "default", ""); err != nil { - t.Fatalf("%v", err) - } - t.Cleanup(func() { - if err = kubeDeleteHelmRepository(t, repo, "default"); err != nil { - t.Logf("%v", err) - } - }) - // wait until this repo have been indexed and cached up to 10 minutes - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) - defer cancel() - if err := sem.Acquire(ctx, 1); err != nil { - t.Fatalf("Timed out waiting for Redis event: %v", err) - } - } - - t.Logf("Done with third part of the test") - }() - - if keys, err := redisCli.Keys(redisCli.Context(), "helmrepositories:*").Result(); err != nil { - t.Fatalf("%v", err) - } else { - // the cache should only big enough to be able to hold at most (totalRepos-1) of the keys - // one (or more) entries MUST have been evicted - if len(keys) > totalRepos-1 { - t.Fatalf("Expected at most %d keys in cache but got [%s]", totalRepos-1, keys) - } - } - - // not related to low maxmemory but as long as we are here might as well check that - // there is a Unauthenticated failure when there are no credenitals in the request - _, err = fluxPlugin.GetAvailablePackageSummaries(context.TODO(), &corev1.GetAvailablePackageSummariesRequest{}) - if err == nil || status.Code(err) != codes.Unauthenticated { - t.Fatalf("Expected Unauthenticated, got %v", err) - } - - grpcContext, cancel := context.WithTimeout(grpcContext, 60*time.Second) - defer cancel() - resp2, err := fluxPlugin.GetAvailablePackageSummaries(grpcContext, &corev1.GetAvailablePackageSummariesRequest{}) - if err != nil { - t.Fatalf("%v", err) - } - - // we need to make sure that response contains packages from all existing repositories - // regardless whether they're in the cache or not - expected := sets.String{} - for i := 0; i < totalRepos; i++ { - repo := fmt.Sprintf("bitnami-%d", i) - expected.Insert(repo) - } - for _, s := range resp2.AvailablePackageSummaries { - id := strings.Split(s.AvailablePackageRef.Identifier, "/") - expected.Delete(id[0]) - } - - if expected.Len() != 0 { - t.Fatalf("Expected to get packages from these repositories: %s, but did not get any", - expected.List()) - } -} - -// this test is testing a scenario when a repo that takes a long time to index is added -// and while the indexing is in progress this repo is deleted by another request. -// The goal is to make sure that the events are processed by the cache fully in the order -// they were received and the cache does not end up in inconsistent state -func TestKindClusterAddThenDeleteRepo(t *testing.T) { - checkEnv(t) - - redisCli, err := newRedisClientForIntegrationTest(t) - if err != nil { - t.Fatalf("%+v", err) - } - - // now load some large repos (bitnami) - // I didn't want to store a large (10MB) copy of bitnami repo in our git, - // so for now let it fetch from bitnami website - if err = kubeAddHelmRepository(t, "bitnami-1", "https://charts.bitnami.com/bitnami", "default", ""); err != nil { - t.Fatalf("%v", err) - } - // wait until this repo reaches 'Ready' state so that long indexation process kicks in - if err = kubeWaitUntilHelmRepositoryIsReady(t, "bitnami-1", "default"); err != nil { - t.Fatalf("%v", err) - } - - if err = kubeDeleteHelmRepository(t, "bitnami-1", "default"); err != nil { - t.Fatalf("%v", err) - } - - t.Logf("Waiting up to 30 seconds...") - time.Sleep(30 * time.Second) - - if keys, err := redisCli.Keys(redisCli.Context(), "*").Result(); err != nil { - t.Fatalf("%v", err) - } else { - if len(keys) != 0 { - t.Fatalf("Failing due to unexpected state of the cache. Current keys: %s", keys) - } - } -} - -func TestKindClusterRepoWithBasicAuth(t *testing.T) { - fluxPluginClient, _ := checkEnv(t) - - secretName := "podinfo-basic-auth-secret" - repoName := "podinfo-basic-auth" - - if err := kubeCreateSecret(t, newBasicAuthSecret(secretName, "default", "foo", "bar")); err != nil { - t.Fatalf("%v", err) - } - t.Cleanup(func() { - err := kubeDeleteSecret(t, "default", secretName) - if err != nil { - t.Logf("Failed to delete helm repository due to [%v]", err) - } - }) - - if err := kubeAddHelmRepository(t, repoName, podinfo_basic_auth_repo_url, "default", secretName); err != nil { - t.Fatalf("%v", err) - } - t.Cleanup(func() { - err := kubeDeleteHelmRepository(t, repoName, "default") - if err != nil { - t.Logf("Failed to delete helm repository due to [%v]", err) - } - }) - - // wait until this repo reaches 'Ready' - if err := kubeWaitUntilHelmRepositoryIsReady(t, repoName, "default"); err != nil { - t.Fatalf("%v", err) - } - - grpcContext := newGrpcAdminContext(t, "test-create-admin-basic-auth") - - const maxWait = 25 - for i := 0; i <= maxWait; i++ { - grpcContext, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) - defer cancel() - resp, err := fluxPluginClient.GetAvailablePackageSummaries( - grpcContext, - &corev1.GetAvailablePackageSummariesRequest{ - Context: &corev1.Context{ - Namespace: "default", - }, - }) - if err == nil { - opt1 := cmpopts.IgnoreUnexported( - corev1.GetAvailablePackageSummariesResponse{}, - corev1.AvailablePackageSummary{}, - corev1.AvailablePackageReference{}, - corev1.Context{}, - plugins.Plugin{}, - corev1.PackageAppVersion{}) - opt2 := cmpopts.SortSlices(lessAvailablePackageFunc) - if got, want := resp, available_package_summaries_podinfo_basic_auth; !cmp.Equal(got, want, opt1, opt2) { - t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1, opt2)) - } - break - } else if i == maxWait { - t.Fatalf("Timed out waiting for available package summaries, last response: %v, last error: [%v]", resp, err) - } else { - t.Logf("Waiting 2s for repository [%s] to be indexed, attempt [%d/%d]...", repoName, i+1, maxWait) - time.Sleep(2 * time.Second) - } - } - - availablePackageRef := availableRef(repoName+"/podinfo", "default") - - // first try the negative case, no auth - should fail due to not being able to - // read secrets in all namespaces - fluxPluginServiceAccount := "test-repo-with-basic-auth" - ctx, cancel := context.WithTimeout(newGrpcFluxPluginContext(t, fluxPluginServiceAccount), defaultContextTimeout) - defer cancel() - _, err := fluxPluginClient.GetAvailablePackageDetail( - ctx, - &corev1.GetAvailablePackageDetailRequest{AvailablePackageRef: availablePackageRef}) - if err == nil { - t.Fatalf("Expected error, did not get one") - } else if status.Code(err) != codes.PermissionDenied { - t.Fatalf("GetAvailablePackageDetailRequest expected: PermissionDenied, got: %v", err) - } - - // this should succeed as it is done in the context of cluster admin - grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout) - defer cancel() - resp, err := fluxPluginClient.GetAvailablePackageDetail( - grpcContext, - &corev1.GetAvailablePackageDetailRequest{AvailablePackageRef: availablePackageRef}) - if err != nil { - t.Fatalf("%v", err) - } - - compareActualVsExpectedAvailablePackageDetail(t, resp.AvailablePackageDetail, expected_detail_podinfo_basic_auth.AvailablePackageDetail) -} - -type integrationTestAddRepoSpec struct { - testName string - request *corev1.AddPackageRepositoryRequest - existingSecret *apiv1.Secret - expectedResponse *corev1.AddPackageRepositoryResponse - expectedStatusCode codes.Code - expectedReconcileFailure bool -} - -func TestKindClusterAddPackageRepository(t *testing.T) { - _, fluxPluginReposClient := checkEnv(t) - - testCases := []integrationTestAddRepoSpec{ - { - testName: "add repo test (simplest case)", - request: &corev1.AddPackageRepositoryRequest{ - Name: "my-podinfo", - Context: &corev1.Context{Namespace: "default"}, - Type: "helm", - Url: podinfo_repo_url, - }, - expectedResponse: &corev1.AddPackageRepositoryResponse{ - PackageRepoRef: &corev1.PackageRepositoryReference{ - Context: &corev1.Context{ - Namespace: "default", - Cluster: KubeappsCluster, - }, - Identifier: "my-podinfo", - Plugin: fluxPlugin, - }, - }, - expectedStatusCode: codes.OK, - }, - { - testName: "package repository with basic auth", - request: &corev1.AddPackageRepositoryRequest{ - Name: "my-podinfo-2", - Context: &corev1.Context{Namespace: "default"}, - Type: "helm", - Url: podinfo_basic_auth_repo_url, - Auth: &corev1.PackageRepositoryAuth{ - Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, - PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{ - UsernamePassword: &corev1.UsernamePassword{ - Username: "foo", - Password: "bar", - }, - }, - }, - }, - expectedResponse: &corev1.AddPackageRepositoryResponse{ - PackageRepoRef: &corev1.PackageRepositoryReference{ - Context: &corev1.Context{ - Namespace: "default", - Cluster: KubeappsCluster, - }, - Identifier: "my-podinfo-2", - Plugin: fluxPlugin, - }, - }, - expectedStatusCode: codes.OK, - }, - { - testName: "package repository with wrong basic auth fails", - request: &corev1.AddPackageRepositoryRequest{ - Name: "my-podinfo-3", - Context: &corev1.Context{Namespace: "default"}, - Type: "helm", - Url: podinfo_basic_auth_repo_url, - Auth: &corev1.PackageRepositoryAuth{ - Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, - PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{ - UsernamePassword: &corev1.UsernamePassword{ - Username: "foo", - Password: "bar-2", - }, - }, - }, - }, - expectedResponse: &corev1.AddPackageRepositoryResponse{ - PackageRepoRef: &corev1.PackageRepositoryReference{ - Context: &corev1.Context{ - Namespace: "default", - Cluster: KubeappsCluster, - }, - Identifier: "my-podinfo-3", - Plugin: fluxPlugin, - }, - }, - expectedStatusCode: codes.OK, - expectedReconcileFailure: true, - }, - { - testName: "package repository with basic auth and existing secret", - request: &corev1.AddPackageRepositoryRequest{ - Name: "my-podinfo-4", - Context: &corev1.Context{Namespace: "default"}, - Type: "helm", - Url: podinfo_basic_auth_repo_url, - Auth: &corev1.PackageRepositoryAuth{ - Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, - PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_SecretRef{ - SecretRef: &corev1.SecretKeyReference{ - Name: "secret-1", - }, - }, - }, - }, - existingSecret: newBasicAuthSecret("secret-1", "default", "foo", "bar"), - expectedResponse: &corev1.AddPackageRepositoryResponse{ - PackageRepoRef: &corev1.PackageRepositoryReference{ - Context: &corev1.Context{ - Namespace: "default", - Cluster: KubeappsCluster, - }, - Identifier: "my-podinfo-4", - Plugin: fluxPlugin, - }, - }, - expectedStatusCode: codes.OK, - }, - } - - grpcContext := newGrpcAdminContext(t, "test-add-repo-admin") - - for _, tc := range testCases { - t.Run(tc.testName, func(t *testing.T) { - ctx, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) - defer cancel() - if tc.existingSecret != nil { - if err := kubeCreateSecret(t, tc.existingSecret); err != nil { - t.Fatalf("%v", err) - } - t.Cleanup(func() { - err := kubeDeleteSecret(t, tc.existingSecret.Namespace, tc.existingSecret.Name) - if err != nil { - t.Logf("Failed to delete secret due to [%v]", err) - } - }) - } - resp, err := fluxPluginReposClient.AddPackageRepository(ctx, tc.request) - if tc.expectedStatusCode != codes.OK { - if status.Code(err) != tc.expectedStatusCode { - t.Fatalf("Expected %v, got: %v", tc.expectedStatusCode, err) - } - return // done, nothing more to check - } else if err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - err := kubeDeleteHelmRepository(t, tc.request.Name, tc.request.Context.Namespace) - if err != nil { - t.Logf("Failed to delete helm source due to [%v]", err) - } - }) - opt1 := cmpopts.IgnoreUnexported( - corev1.AddPackageRepositoryResponse{}, - corev1.Context{}, - corev1.PackageRepositoryReference{}, - plugins.Plugin{}, - ) - if got, want := resp, tc.expectedResponse; !cmp.Equal(got, want, opt1) { - t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1)) - } - - // TODO wait for reconcile. To do it properly, we need "R" in CRUD to be - // designed and implemented - err = kubeWaitUntilHelmRepositoryIsReady(t, tc.request.Name, tc.request.Context.Namespace) - if err != nil && !tc.expectedReconcileFailure { - t.Fatal(err) - } else if err == nil && tc.expectedReconcileFailure { - t.Fatalf("Expected error got nil") - } - }) - } -} - func createAndWaitForHelmRelease(t *testing.T, tc integrationTestCreatePackageSpec, fluxPluginClient fluxplugin.FluxV2PackagesServiceClient, grpcContext context.Context) *corev1.InstalledPackageReference { availablePackageRef := tc.request.AvailablePackageRef idParts := strings.Split(availablePackageRef.Identifier, "/") @@ -1793,35 +1252,4 @@ var ( Cluster: KubeappsCluster, }, } - - available_package_summaries_podinfo_basic_auth = &corev1.GetAvailablePackageSummariesResponse{ - AvailablePackageSummaries: []*corev1.AvailablePackageSummary{ - { - Name: "podinfo", - AvailablePackageRef: availableRef("podinfo-basic-auth/podinfo", "default"), - LatestVersion: &corev1.PackageAppVersion{PkgVersion: "6.0.0", AppVersion: "6.0.0"}, - DisplayName: "podinfo", - ShortDescription: "Podinfo Helm chart for Kubernetes", - Categories: []string{""}, - }, - }, - } - - expected_detail_podinfo_basic_auth = &corev1.GetAvailablePackageDetailResponse{ - AvailablePackageDetail: &corev1.AvailablePackageDetail{ - AvailablePackageRef: availableRef("podinfo-basic-auth/podinfo", "default"), - Name: "podinfo", - Version: &corev1.PackageAppVersion{PkgVersion: "6.0.0", AppVersion: "6.0.0"}, - RepoUrl: "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo-basic-auth", - HomeUrl: "https://github.com/stefanprodan/podinfo", - DisplayName: "podinfo", - ShortDescription: "Podinfo Helm chart for Kubernetes", - SourceUrls: []string{"https://github.com/stefanprodan/podinfo"}, - Maintainers: []*corev1.Maintainer{ - {Name: "stefanprodan", Email: "stefanprodan@users.noreply.github.com"}, - }, - Readme: "Podinfo is used by CNCF projects like [Flux](https://github.com/fluxcd/flux2)", - DefaultValues: "Default values for podinfo.\n\nreplicaCount: 1\n", - }, - } ) diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_test.go index d203f5699ce..c99a467fa4d 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/release_test.go @@ -1415,7 +1415,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1463,7 +1463,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1499,7 +1499,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1541,7 +1541,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/airflow-many-versions.yaml", chartName: "airflow", - chartTarGz: "testdata/airflow-6.7.1.tgz", + chartTarGz: "testdata/charts/airflow-6.7.1.tgz", chartSpecVersion: "6.7.1", chartArtifactVersion: "6.7.1", releaseName: "my-airflow", @@ -1574,7 +1574,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/airflow-many-versions.yaml", chartName: "airflow", - chartTarGz: "testdata/airflow-6.7.1.tgz", + chartTarGz: "testdata/charts/airflow-6.7.1.tgz", chartSpecVersion: "<=6.7.1", chartArtifactVersion: "6.7.1", releaseName: "my-airflow", @@ -1607,7 +1607,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1632,7 +1632,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1666,7 +1666,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "*", chartArtifactVersion: "14.4.0", releaseName: "my-redis", @@ -1950,7 +1950,7 @@ var ( repoNamespace: "default", repoIndex: "testdata/redis-many-versions.yaml", chartName: "redis", - chartTarGz: "testdata/redis-14.4.0.tgz", + chartTarGz: "testdata/charts/redis-14.4.0.tgz", chartSpecVersion: "14.4.0", chartArtifactVersion: "14.4.0", releaseName: "my-redis", diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go index 829c0488cb2..6200d8b163c 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo.go @@ -222,6 +222,8 @@ func (s *Server) newRepo(ctx context.Context, targetName types.NamespacedName, u } } + passCredentials := auth != nil && auth.PassCredentials + secretRef, checkSecret := "", false if secret != nil { // create a secret first, if applicable @@ -246,8 +248,9 @@ func (s *Server) newRepo(ctx context.Context, targetName types.NamespacedName, u } else if _, err = typedClient.CoreV1().Secrets(targetName.Namespace).Get(ctx, secretRef, metav1.GetOptions{}); err != nil { return nil, statuserror.FromK8sError("get", "secret", secretRef, err) } - // TODO (gfichtenholt) also check that the secret type corresponds to specified auth type, - // e.g. if AuthType is PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, + // TODO (gfichtenholt) also check that the data in the opaque secret corresponds + // to specified auth type, e.g. if AuthType is + // PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, // check that the secret has "username" and "password" fields, etc. // TODO (gfichtenholt) @@ -258,7 +261,7 @@ func (s *Server) newRepo(ctx context.Context, targetName types.NamespacedName, u // the "username" and "password" fields are present). } - if fluxRepo, err := s.newFluxHelmRepo(targetName, url, interval, secretRef); err != nil { + if fluxRepo, err := s.newFluxHelmRepo(targetName, url, interval, secretRef, passCredentials); err != nil { return nil, err } else if client, err := s.getClient(ctx, targetName.Namespace); err != nil { return nil, err @@ -277,7 +280,12 @@ func (s *Server) newRepo(ctx context.Context, targetName types.NamespacedName, u } // ref https://fluxcd.io/docs/components/source/helmrepositories/ -func (s *Server) newFluxHelmRepo(targetName types.NamespacedName, url string, interval uint32, secretRef string) (*sourcev1.HelmRepository, error) { +func (s *Server) newFluxHelmRepo( + targetName types.NamespacedName, + url string, + interval uint32, + secretRef string, + passCredentials bool) (*sourcev1.HelmRepository, error) { pollInterval := defaultPollInterval if interval > 0 { pollInterval = metav1.Duration{Duration: time.Duration(interval) * time.Second} @@ -301,6 +309,9 @@ func (s *Server) newFluxHelmRepo(targetName types.NamespacedName, url string, in Name: secretRef, } } + if passCredentials { + fluxRepo.Spec.PassCredentials = true + } return fluxRepo, nil } diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_integration_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_integration_test.go new file mode 100644 index 00000000000..e539070b03d --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_integration_test.go @@ -0,0 +1,403 @@ +// Copyright 2021-2022 the Kubeapps contributors. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + corev1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1" + plugins "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/plugins/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + apiv1 "k8s.io/api/core/v1" +) + +// This is an integration test: it tests the full integration of flux plugin with flux back-end +// To run these tests, enable ENABLE_FLUX_INTEGRATION_TESTS variable +// pre-requisites for these tests to run: +// 1) kind cluster with flux deployed +// 2) kubeapps apis apiserver service running with fluxv2 plug-in enabled, port forwarded to 8080, e.g. +// kubectl -n kubeapps port-forward svc/kubeapps-internal-kubeappsapis 8080:8080 +// 3) run './kind-cluster-setup.sh deploy' once prior to these tests + +// this test is testing a scenario when a repo that takes a long time to index is added +// and while the indexing is in progress this repo is deleted by another request. +// The goal is to make sure that the events are processed by the cache fully in the order +// they were received and the cache does not end up in inconsistent state +func TestKindClusterAddThenDeleteRepo(t *testing.T) { + checkEnv(t) + + redisCli, err := newRedisClientForIntegrationTest(t) + if err != nil { + t.Fatalf("%+v", err) + } + + // now load some large repos (bitnami) + // I didn't want to store a large (10MB) copy of bitnami repo in our git, + // so for now let it fetch from bitnami website + if err = kubeAddHelmRepository(t, "bitnami-1", "https://charts.bitnami.com/bitnami", "default", ""); err != nil { + t.Fatalf("%v", err) + } + // wait until this repo reaches 'Ready' state so that long indexation process kicks in + if err = kubeWaitUntilHelmRepositoryIsReady(t, "bitnami-1", "default"); err != nil { + t.Fatalf("%v", err) + } + + if err = kubeDeleteHelmRepository(t, "bitnami-1", "default"); err != nil { + t.Fatalf("%v", err) + } + + t.Logf("Waiting up to 30 seconds...") + time.Sleep(30 * time.Second) + + if keys, err := redisCli.Keys(redisCli.Context(), "*").Result(); err != nil { + t.Fatalf("%v", err) + } else { + if len(keys) != 0 { + t.Fatalf("Failing due to unexpected state of the cache. Current keys: %s", keys) + } + } +} + +func TestKindClusterRepoWithBasicAuth(t *testing.T) { + fluxPluginClient, _ := checkEnv(t) + + secretName := "podinfo-basic-auth-secret" + repoName := "podinfo-basic-auth" + + if err := kubeCreateSecret(t, newBasicAuthSecret(secretName, "default", "foo", "bar")); err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + err := kubeDeleteSecret(t, "default", secretName) + if err != nil { + t.Logf("Failed to delete helm repository due to [%v]", err) + } + }) + + if err := kubeAddHelmRepository(t, repoName, podinfo_basic_auth_repo_url, "default", secretName); err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + err := kubeDeleteHelmRepository(t, repoName, "default") + if err != nil { + t.Logf("Failed to delete helm repository due to [%v]", err) + } + }) + + // wait until this repo reaches 'Ready' + if err := kubeWaitUntilHelmRepositoryIsReady(t, repoName, "default"); err != nil { + t.Fatalf("%v", err) + } + + grpcContext := newGrpcAdminContext(t, "test-create-admin-basic-auth") + + const maxWait = 25 + for i := 0; i <= maxWait; i++ { + grpcContext, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) + defer cancel() + resp, err := fluxPluginClient.GetAvailablePackageSummaries( + grpcContext, + &corev1.GetAvailablePackageSummariesRequest{ + Context: &corev1.Context{ + Namespace: "default", + }, + }) + if err == nil { + opt1 := cmpopts.IgnoreUnexported( + corev1.GetAvailablePackageSummariesResponse{}, + corev1.AvailablePackageSummary{}, + corev1.AvailablePackageReference{}, + corev1.Context{}, + plugins.Plugin{}, + corev1.PackageAppVersion{}) + opt2 := cmpopts.SortSlices(lessAvailablePackageFunc) + if got, want := resp, available_package_summaries_podinfo_basic_auth; !cmp.Equal(got, want, opt1, opt2) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1, opt2)) + } + break + } else if i == maxWait { + t.Fatalf("Timed out waiting for available package summaries, last response: %v, last error: [%v]", resp, err) + } else { + t.Logf("Waiting 2s for repository [%s] to be indexed, attempt [%d/%d]...", repoName, i+1, maxWait) + time.Sleep(2 * time.Second) + } + } + + availablePackageRef := availableRef(repoName+"/podinfo", "default") + + // first try the negative case, no auth - should fail due to not being able to + // read secrets in all namespaces + fluxPluginServiceAccount := "test-repo-with-basic-auth" + ctx, cancel := context.WithTimeout(newGrpcFluxPluginContext(t, fluxPluginServiceAccount), defaultContextTimeout) + defer cancel() + _, err := fluxPluginClient.GetAvailablePackageDetail( + ctx, + &corev1.GetAvailablePackageDetailRequest{AvailablePackageRef: availablePackageRef}) + if err == nil { + t.Fatalf("Expected error, did not get one") + } else if status.Code(err) != codes.PermissionDenied { + t.Fatalf("GetAvailablePackageDetailRequest expected: PermissionDenied, got: %v", err) + } + + // this should succeed as it is done in the context of cluster admin + grpcContext, cancel = context.WithTimeout(grpcContext, defaultContextTimeout) + defer cancel() + resp, err := fluxPluginClient.GetAvailablePackageDetail( + grpcContext, + &corev1.GetAvailablePackageDetailRequest{AvailablePackageRef: availablePackageRef}) + if err != nil { + t.Fatalf("%v", err) + } + + compareActualVsExpectedAvailablePackageDetail(t, resp.AvailablePackageDetail, expected_detail_podinfo_basic_auth.AvailablePackageDetail) +} + +type integrationTestAddRepoSpec struct { + testName string + request *corev1.AddPackageRepositoryRequest + existingSecret *apiv1.Secret + expectedResponse *corev1.AddPackageRepositoryResponse + expectedStatusCode codes.Code + expectedReconcileFailure bool +} + +func TestKindClusterAddPackageRepository(t *testing.T) { + _, fluxPluginReposClient := checkEnv(t) + + // these will be used further on for TLS-related scenarios. Init + // byte arrays up front so they can be re-used in multiple places later + ca, pub, priv := getCertsForTesting(t) + + testCases := []integrationTestAddRepoSpec{ + { + testName: "add repo test (simplest case)", + request: &corev1.AddPackageRepositoryRequest{ + Name: "my-podinfo", + Context: &corev1.Context{Namespace: "default"}, + Type: "helm", + Url: podinfo_repo_url, + }, + expectedResponse: &corev1.AddPackageRepositoryResponse{ + PackageRepoRef: &corev1.PackageRepositoryReference{ + Context: &corev1.Context{ + Namespace: "default", + Cluster: KubeappsCluster, + }, + Identifier: "my-podinfo", + Plugin: fluxPlugin, + }, + }, + expectedStatusCode: codes.OK, + }, + { + testName: "package repository with basic auth", + request: &corev1.AddPackageRepositoryRequest{ + Name: "my-podinfo-2", + Context: &corev1.Context{Namespace: "default"}, + Type: "helm", + Url: podinfo_basic_auth_repo_url, + Auth: &corev1.PackageRepositoryAuth{ + Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, + PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{ + UsernamePassword: &corev1.UsernamePassword{ + Username: "foo", + Password: "bar", + }, + }, + }, + }, + expectedResponse: &corev1.AddPackageRepositoryResponse{ + PackageRepoRef: &corev1.PackageRepositoryReference{ + Context: &corev1.Context{ + Namespace: "default", + Cluster: KubeappsCluster, + }, + Identifier: "my-podinfo-2", + Plugin: fluxPlugin, + }, + }, + expectedStatusCode: codes.OK, + }, + { + testName: "package repository with wrong basic auth fails", + request: &corev1.AddPackageRepositoryRequest{ + Name: "my-podinfo-3", + Context: &corev1.Context{Namespace: "default"}, + Type: "helm", + Url: podinfo_basic_auth_repo_url, + Auth: &corev1.PackageRepositoryAuth{ + Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, + PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_UsernamePassword{ + UsernamePassword: &corev1.UsernamePassword{ + Username: "foo", + Password: "bar-2", + }, + }, + }, + }, + expectedResponse: &corev1.AddPackageRepositoryResponse{ + PackageRepoRef: &corev1.PackageRepositoryReference{ + Context: &corev1.Context{ + Namespace: "default", + Cluster: KubeappsCluster, + }, + Identifier: "my-podinfo-3", + Plugin: fluxPlugin, + }, + }, + expectedStatusCode: codes.OK, + expectedReconcileFailure: true, + }, + { + testName: "package repository with basic auth and existing secret", + request: &corev1.AddPackageRepositoryRequest{ + Name: "my-podinfo-4", + Context: &corev1.Context{Namespace: "default"}, + Type: "helm", + Url: podinfo_basic_auth_repo_url, + Auth: &corev1.PackageRepositoryAuth{ + Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_BASIC_AUTH, + PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_SecretRef{ + SecretRef: &corev1.SecretKeyReference{ + Name: "secret-1", + }, + }, + }, + }, + existingSecret: newBasicAuthSecret("secret-1", "default", "foo", "bar"), + expectedResponse: &corev1.AddPackageRepositoryResponse{ + PackageRepoRef: &corev1.PackageRepositoryReference{ + Context: &corev1.Context{ + Namespace: "default", + Cluster: KubeappsCluster, + }, + Identifier: "my-podinfo-4", + Plugin: fluxPlugin, + }, + }, + expectedStatusCode: codes.OK, + }, + { + testName: "package repository with TLS", + request: &corev1.AddPackageRepositoryRequest{ + Name: "my-podinfo-4", + Context: &corev1.Context{Namespace: "default"}, + Type: "helm", + Url: podinfo_tls_repo_url, + Auth: &corev1.PackageRepositoryAuth{ + Type: corev1.PackageRepositoryAuth_PACKAGE_REPOSITORY_AUTH_TYPE_TLS, + PackageRepoAuthOneOf: &corev1.PackageRepositoryAuth_SecretRef{ + SecretRef: &corev1.SecretKeyReference{ + Name: "secret-2", + }, + }, + }, + }, + existingSecret: newTlsSecret("secret-2", "default", pub, priv, ca), + expectedResponse: &corev1.AddPackageRepositoryResponse{ + PackageRepoRef: &corev1.PackageRepositoryReference{ + Context: &corev1.Context{ + Namespace: "default", + Cluster: KubeappsCluster, + }, + Identifier: "my-podinfo-4", + Plugin: fluxPlugin, + }, + }, + expectedStatusCode: codes.OK, + }, + } + + grpcContext := newGrpcAdminContext(t, "test-add-repo-admin") + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + ctx, cancel := context.WithTimeout(grpcContext, defaultContextTimeout) + defer cancel() + if tc.existingSecret != nil { + if err := kubeCreateSecret(t, tc.existingSecret); err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + err := kubeDeleteSecret(t, tc.existingSecret.Namespace, tc.existingSecret.Name) + if err != nil { + t.Logf("Failed to delete secret due to [%v]", err) + } + }) + } + resp, err := fluxPluginReposClient.AddPackageRepository(ctx, tc.request) + if tc.expectedStatusCode != codes.OK { + if status.Code(err) != tc.expectedStatusCode { + t.Fatalf("Expected %v, got: %v", tc.expectedStatusCode, err) + } + return // done, nothing more to check + } else if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + err := kubeDeleteHelmRepository(t, tc.request.Name, tc.request.Context.Namespace) + if err != nil { + t.Logf("Failed to delete helm source due to [%v]", err) + } + }) + opt1 := cmpopts.IgnoreUnexported( + corev1.AddPackageRepositoryResponse{}, + corev1.Context{}, + corev1.PackageRepositoryReference{}, + plugins.Plugin{}, + ) + if got, want := resp, tc.expectedResponse; !cmp.Equal(got, want, opt1) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, opt1)) + } + + // TODO wait for reconcile. To do it properly, we need "R" in CRUD to be + // designed and implemented + err = kubeWaitUntilHelmRepositoryIsReady(t, tc.request.Name, tc.request.Context.Namespace) + if err != nil && !tc.expectedReconcileFailure { + t.Fatal(err) + } else if err == nil && tc.expectedReconcileFailure { + t.Fatalf("Expected error got nil") + } + }) + } +} + +// global vars +var ( + available_package_summaries_podinfo_basic_auth = &corev1.GetAvailablePackageSummariesResponse{ + AvailablePackageSummaries: []*corev1.AvailablePackageSummary{ + { + Name: "podinfo", + AvailablePackageRef: availableRef("podinfo-basic-auth/podinfo", "default"), + LatestVersion: &corev1.PackageAppVersion{PkgVersion: "6.0.0", AppVersion: "6.0.0"}, + DisplayName: "podinfo", + ShortDescription: "Podinfo Helm chart for Kubernetes", + Categories: []string{""}, + }, + }, + } + + expected_detail_podinfo_basic_auth = &corev1.GetAvailablePackageDetailResponse{ + AvailablePackageDetail: &corev1.AvailablePackageDetail{ + AvailablePackageRef: availableRef("podinfo-basic-auth/podinfo", "default"), + Name: "podinfo", + Version: &corev1.PackageAppVersion{PkgVersion: "6.0.0", AppVersion: "6.0.0"}, + RepoUrl: "http://fluxv2plugin-testdata-svc.default.svc.cluster.local:80/podinfo-basic-auth", + HomeUrl: "https://github.com/stefanprodan/podinfo", + DisplayName: "podinfo", + ShortDescription: "Podinfo Helm chart for Kubernetes", + SourceUrls: []string{"https://github.com/stefanprodan/podinfo"}, + Maintainers: []*corev1.Maintainer{ + {Name: "stefanprodan", Email: "stefanprodan@users.noreply.github.com"}, + }, + Readme: "Podinfo is used by CNCF projects like [Flux](https://github.com/fluxcd/flux2)", + DefaultValues: "Default values for podinfo.\n\nreplicaCount: 1\n", + }, + } +) diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_test.go index b43edfc5984..1b2722cb1de 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/repo_test.go @@ -1192,15 +1192,9 @@ func TestGetAvailablePackageSummariesAfterCacheResyncQueueIdle(t *testing.T) { func TestAddPackageRepository(t *testing.T) { - var ca, pub, priv []byte - var err error - if ca, err = ioutil.ReadFile("testdata/rootCA.crt"); err != nil { - t.Fatalf("%+v", err) - } else if pub, err = ioutil.ReadFile("testdata/crt.pem"); err != nil { - t.Fatalf("%+v", err) - } else if priv, err = ioutil.ReadFile("testdata/key.pem"); err != nil { - t.Fatalf("%+v", err) - } + // these will be used further on for TLS-related scenarios. Init + // byte arrays up front so they can be re-used in multiple places later + ca, pub, priv := getCertsForTesting(t) testCases := []struct { name string @@ -1329,7 +1323,7 @@ func TestAddPackageRepository(t *testing.T) { statusCode: codes.NotFound, }, { - name: "package repository with basic auth", + name: "package repository with basic auth and pass_credentials flag", request: &corev1.AddPackageRepositoryRequest{ Name: "bar", Context: &corev1.Context{Namespace: "foo"}, @@ -1343,10 +1337,11 @@ func TestAddPackageRepository(t *testing.T) { Password: "zot", }, }, + PassCredentials: true, }, }, expectedResponse: add_repo_expected_resp, - expectedRepo: &add_repo_2, + expectedRepo: &add_repo_4, expectedCreatedSecret: newBasicAuthSecret("bar-", "foo", "baz", "zot"), statusCode: codes.OK, }, @@ -1789,17 +1784,17 @@ var ( valid_index_charts_spec = []testSpecChartWithFile{ { name: "acs-engine-autoscaler", - tgzFile: "testdata/acs-engine-autoscaler-2.1.1.tgz", + tgzFile: "testdata/charts/acs-engine-autoscaler-2.1.1.tgz", revision: "2.1.1", }, { name: "wordpress", - tgzFile: "testdata/wordpress-0.7.5.tgz", + tgzFile: "testdata/charts/wordpress-0.7.5.tgz", revision: "0.7.5", }, { name: "wordpress", - tgzFile: "testdata/wordpress-0.7.4.tgz", + tgzFile: "testdata/charts/wordpress-0.7.4.tgz", revision: "0.7.4", }, } @@ -2010,6 +2005,24 @@ var ( }, } + add_repo_4 = sourcev1.HelmRepository{ + TypeMeta: metav1.TypeMeta{ + Kind: sourcev1.HelmRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + ResourceVersion: "1", + }, + Spec: sourcev1.HelmRepositorySpec{ + URL: "http://example.com", + Interval: metav1.Duration{Duration: 10 * time.Minute}, + SecretRef: &fluxmeta.LocalObjectReference{Name: "bar-"}, + PassCredentials: true, + }, + } + add_repo_expected_resp = &corev1.AddPackageRepositoryResponse{ PackageRepoRef: &corev1.PackageRepositoryReference{ Context: &corev1.Context{ diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/test_util_test.go b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/test_util_test.go index 6375d27f1c8..ec48b6a9d20 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/test_util_test.go +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/test_util_test.go @@ -7,6 +7,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "io/ioutil" "net/http" "reflect" "testing" @@ -145,6 +146,19 @@ func compareJSONStrings(t *testing.T, expectedJSONString, actualJSONString strin } } +// generate-cert.sh script in testdata directory is used to generate these files +func getCertsForTesting(t *testing.T) (ca, pub, priv []byte) { + var err error + if ca, err = ioutil.ReadFile("testdata/cert/ca.pem"); err != nil { + t.Fatalf("%+v", err) + } else if pub, err = ioutil.ReadFile("testdata/cert/server.pem"); err != nil { + t.Fatalf("%+v", err) + } else if priv, err = ioutil.ReadFile("testdata/cert/server-key.pem"); err != nil { + t.Fatalf("%+v", err) + } + return ca, pub, priv +} + // ref: https://stackoverflow.com/questions/21936332/idiomatic-way-of-requiring-http-basic-auth-in-go func basicAuth(handler http.HandlerFunc, username, password, realm string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/Dockerfile b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/Dockerfile index c6e9e599d5f..fd5d08bfe8f 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/Dockerfile +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/Dockerfile @@ -10,9 +10,15 @@ COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./.htpasswd /etc/apache2/.htpasswd COPY ./podinfo-index.yaml /usr/share/nginx/html/podinfo/index.yaml -COPY ./podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo/ -COPY ./podinfo-5.2.1.tgz /usr/share/nginx/html/podinfo/ +COPY ./charts/podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo/ +COPY ./charts/podinfo-5.2.1.tgz /usr/share/nginx/html/podinfo/ COPY ./podinfo-basic-auth-index.yaml /usr/share/nginx/html/podinfo-basic-auth/index.yaml -COPY ./podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo-basic-auth/ -COPY ./podinfo-5.2.1.tgz /usr/share/nginx/html/podinfo-basic-auth/ +COPY ./charts/podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo-basic-auth/ +COPY ./charts/podinfo-5.2.1.tgz /usr/share/nginx/html/podinfo-basic-auth/ + +COPY ./cert/ssl-bundle.pem /etc/ssl/certs/ +COPY ./cert/server-key.pem /etc/ssl/certs/ +COPY ./podinfo-tls-index.yaml /usr/share/nginx/html/podinfo-tls/index.yaml +COPY ./charts/podinfo-6.0.0.tgz /usr/share/nginx/html/podinfo-tls/ +COPY ./charts/podinfo-5.2.1.tgz /usr/share/nginx/html/podinfo-tls/ diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-config.json b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-config.json new file mode 100644 index 00000000000..91c0644c699 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-config.json @@ -0,0 +1,18 @@ +{ + "signing": { + "default": { + "expiry": "87600h" + }, + "profiles": { + "web-servers": { + "usages": [ + "signing", + "key encipherment", + "server auth", + "client auth" + ], + "expiry": "87600h" + } + } + } +} diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-csr.json b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-csr.json new file mode 100644 index 00000000000..04662569780 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-csr.json @@ -0,0 +1,8 @@ +{ + "CN": "fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local CA", + "hosts": [ + "127.0.0.1", + "localhost", + "fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local" + ] +} diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-key.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-key.pem new file mode 100644 index 00000000000..4aad50edb15 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICgAqMyEvPEG1bKeX2TcS+sTlgHpWzJteyekDX1zg1/SoAoGCCqGSM49 +AwEHoUQDQgAERcG/6sc0s7XUmT6yfyRVpxTJIq6oKsd9mjqhPfEAsQtZvy5miD4Q +Dv/AmMOQbBamcDlcj8qs6u83NyrH7jIaMw== +-----END EC PRIVATE KEY----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca.pem new file mode 100644 index 00000000000..57527399c88 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ca.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYWgAwIBAgIUBiiuuxJQ/Elz+YfTWtHzpAX6kLgwCgYIKoZIzj0EAwIw +RTFDMEEGA1UEAxM6Zmx1eHYycGx1Z2luLXRlc3RkYXRhLXNzbC1zdmMuZGVmYXVs +dC5zdmMuY2x1c3Rlci5sb2NhbCBDQTAeFw0yMjAzMDIyMzM3MDBaFw0yNzAzMDEy +MzM3MDBaMEUxQzBBBgNVBAMTOmZsdXh2MnBsdWdpbi10ZXN0ZGF0YS1zc2wtc3Zj +LmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwgQ0EwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAARFwb/qxzSztdSZPrJ/JFWnFMkirqgqx32aOqE98QCxC1m/LmaIPhAO +/8CYw5BsFqZwOVyPyqzq7zc3KsfuMhozo1MwUTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU1AF8ckx4/NE0JpgrTGZrRNbmB68wDwYD +VR0RBAgwBocEfwAAATAKBggqhkjOPQQDAgNJADBGAiEA8Jg71jqePvsZOPLSyUmL +YBuc3LkpE3egCf5zWdVFl0wCIQCTlLshTuBc3SXX2q0z68bneWvPnp1l0cKCR9Lg +QwcBGg== +-----END CERTIFICATE----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/generate-cert.sh b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/generate-cert.sh new file mode 100755 index 00000000000..2ee88085c35 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/generate-cert.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Copyright 2021-2022 the Kubeapps contributors. +# SPDX-License-Identifier: Apache-2.0 + +set -o errexit +set -o nounset +set -o pipefail + +# inspired by https://github.com/fluxcd/source-controller/tree/main/controllers/testdata/certs +# cfssl tool is kind of like openssl, but a little friendlier for simple stuff +# you can install it with brew on MacOS + +# this will generate root cert authority cert (ca.pem) and private key (ca-key.pem) +cfssl gencert -initca ca-csr.json | cfssljson -bare ca - +# this will generate server cert (server.pem) and private key (server-key.pem) +cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=web-servers server-csr.json | cfssljson -bare server +# this will generate the bundle that should be used for nginx ssl server config +cat server.pem ca.pem > ssl-bundle.pem diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-csr.json b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-csr.json new file mode 100644 index 00000000000..5022fa430df --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-csr.json @@ -0,0 +1,8 @@ +{ + "CN": "fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local", + "hosts": [ + "127.0.0.1", + "localhost", + "fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local" + ] +} diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-key.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-key.pem new file mode 100644 index 00000000000..bb93942fd49 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIO1iDVfUeqGKmDdSs6kQqJyaQYbGXaOXe9/niQEOpe0loAoGCCqGSM49 +AwEHoUQDQgAECk2cZkBTcw4JQlaoz5sNDnT7bga4cOMI7t93QNvn9uBYRS7TAUvj +ni4BONDstTF26yc7B5LsqynSOkgVYJaStA== +-----END EC PRIVATE KEY----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server.pem new file mode 100644 index 00000000000..0b6f0e98ed4 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/server.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICXjCCAgWgAwIBAgIUHYDPnjnIE7gu5W5RDvS40PSoVGswCgYIKoZIzj0EAwIw +RTFDMEEGA1UEAxM6Zmx1eHYycGx1Z2luLXRlc3RkYXRhLXNzbC1zdmMuZGVmYXVs +dC5zdmMuY2x1c3Rlci5sb2NhbCBDQTAeFw0yMjAzMDIyMzQyMDBaFw0zMjAyMjgy +MzQyMDBaMEIxQDA+BgNVBAMTN2ZsdXh2MnBsdWdpbi10ZXN0ZGF0YS1zc2wtc3Zj +LmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwwWTATBgcqhkjOPQIBBggqhkjOPQMB +BwNCAAQKTZxmQFNzDglCVqjPmw0OdPtuBrhw4wju33dA2+f24FhFLtMBS+OeLgE4 +0Oy1MXbrJzsHkuyrKdI6SBVglpK0o4HVMIHSMA4GA1UdDwEB/wQEAwIFoDAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E +FgQUKyEA6PY/qMRrf1HW6tx3jskxkvYwHwYDVR0jBBgwFoAU1AF8ckx4/NE0Jpgr +TGZrRNbmB68wUwYDVR0RBEwwSoIJbG9jYWxob3N0gjdmbHV4djJwbHVnaW4tdGVz +dGRhdGEtc3NsLXN2Yy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FshwR/AAABMAoG +CCqGSM49BAMCA0cAMEQCIBTZjKw8G1/OiCj2wkt/0uZkJqkJ3FWdW8Np8Hul5od9 +AiAIR5dSzWB36J6GvYqtL5vnIx4OMpQpwX3haY3wcRaIsQ== +-----END CERTIFICATE----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ssl-bundle.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ssl-bundle.pem new file mode 100644 index 00000000000..9cc0555acc3 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/cert/ssl-bundle.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIICXjCCAgWgAwIBAgIUHYDPnjnIE7gu5W5RDvS40PSoVGswCgYIKoZIzj0EAwIw +RTFDMEEGA1UEAxM6Zmx1eHYycGx1Z2luLXRlc3RkYXRhLXNzbC1zdmMuZGVmYXVs +dC5zdmMuY2x1c3Rlci5sb2NhbCBDQTAeFw0yMjAzMDIyMzQyMDBaFw0zMjAyMjgy +MzQyMDBaMEIxQDA+BgNVBAMTN2ZsdXh2MnBsdWdpbi10ZXN0ZGF0YS1zc2wtc3Zj +LmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwwWTATBgcqhkjOPQIBBggqhkjOPQMB +BwNCAAQKTZxmQFNzDglCVqjPmw0OdPtuBrhw4wju33dA2+f24FhFLtMBS+OeLgE4 +0Oy1MXbrJzsHkuyrKdI6SBVglpK0o4HVMIHSMA4GA1UdDwEB/wQEAwIFoDAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E +FgQUKyEA6PY/qMRrf1HW6tx3jskxkvYwHwYDVR0jBBgwFoAU1AF8ckx4/NE0Jpgr +TGZrRNbmB68wUwYDVR0RBEwwSoIJbG9jYWxob3N0gjdmbHV4djJwbHVnaW4tdGVz +dGRhdGEtc3NsLXN2Yy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FshwR/AAABMAoG +CCqGSM49BAMCA0cAMEQCIBTZjKw8G1/OiCj2wkt/0uZkJqkJ3FWdW8Np8Hul5od9 +AiAIR5dSzWB36J6GvYqtL5vnIx4OMpQpwX3haY3wcRaIsQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB4DCCAYWgAwIBAgIUBiiuuxJQ/Elz+YfTWtHzpAX6kLgwCgYIKoZIzj0EAwIw +RTFDMEEGA1UEAxM6Zmx1eHYycGx1Z2luLXRlc3RkYXRhLXNzbC1zdmMuZGVmYXVs +dC5zdmMuY2x1c3Rlci5sb2NhbCBDQTAeFw0yMjAzMDIyMzM3MDBaFw0yNzAzMDEy +MzM3MDBaMEUxQzBBBgNVBAMTOmZsdXh2MnBsdWdpbi10ZXN0ZGF0YS1zc2wtc3Zj +LmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwgQ0EwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAARFwb/qxzSztdSZPrJ/JFWnFMkirqgqx32aOqE98QCxC1m/LmaIPhAO +/8CYw5BsFqZwOVyPyqzq7zc3KsfuMhozo1MwUTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU1AF8ckx4/NE0JpgrTGZrRNbmB68wDwYD +VR0RBAgwBocEfwAAATAKBggqhkjOPQQDAgNJADBGAiEA8Jg71jqePvsZOPLSyUmL +YBuc3LkpE3egCf5zWdVFl0wCIQCTlLshTuBc3SXX2q0z68bneWvPnp1l0cKCR9Lg +QwcBGg== +-----END CERTIFICATE----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/acs-engine-autoscaler-2.1.1.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/acs-engine-autoscaler-2.1.1.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/acs-engine-autoscaler-2.1.1.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/acs-engine-autoscaler-2.1.1.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/airflow-6.7.1.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/airflow-6.7.1.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/airflow-6.7.1.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/airflow-6.7.1.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-5.2.1.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/podinfo-5.2.1.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-5.2.1.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/podinfo-5.2.1.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-6.0.0.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/podinfo-6.0.0.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-6.0.0.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/podinfo-6.0.0.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-14.3.4.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/redis-14.3.4.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-14.3.4.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/redis-14.3.4.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-14.4.0.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/redis-14.4.0.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-14.4.0.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/redis-14.4.0.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/wordpress-0.7.4.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/wordpress-0.7.4.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/wordpress-0.7.4.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/wordpress-0.7.4.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/wordpress-0.7.5.tgz b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/wordpress-0.7.5.tgz similarity index 100% rename from cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/wordpress-0.7.5.tgz rename to cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/charts/wordpress-0.7.5.tgz diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/crt.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/crt.pem deleted file mode 100644 index 715cd0f6561..00000000000 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/crt.pem +++ /dev/null @@ -1,73 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 55:31:53:9b:41:72:05:dc:90:49:bd:48:13:7c:59:9e:5a:53:5e:86 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=CO, L=Boulder, O=Helm, CN=helm.sh - Validity - Not Before: Nov 1 22:51:49 2019 GMT - Not After : Oct 29 22:51:49 2029 GMT - Subject: C=US, ST=CO, L=Boulder, O=Helm, CN=helm.sh - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:c8:89:55:0d:0b:f1:da:e6:c0:70:7d:d3:27:cd: - b8:a8:81:8b:7c:a4:89:e5:d1:b1:78:01:1d:df:44: - 88:0b:fc:d6:81:35:3d:d1:3b:5e:8f:bb:93:b3:7e: - 28:db:ed:ff:a0:13:3a:70:a3:fe:94:6b:0b:fe:fb: - 63:00:b0:cb:dc:81:cd:80:dc:d0:2f:bf:b2:4f:9a: - 81:d4:22:dc:97:c8:8f:27:86:59:91:fa:92:05:75: - c4:cc:6b:f5:a9:6b:74:1e:f5:db:a9:f8:bf:8c:a2: - 25:fd:a0:cc:79:f4:25:57:74:a9:23:9b:e2:b7:22: - 7a:14:7a:3d:ea:f1:7e:32:6b:57:6c:2e:c6:4f:75: - 54:f9:6b:54:d2:ca:eb:54:1c:af:39:15:9b:d0:7c: - 0f:f8:55:51:04:ea:da:fa:7b:8b:63:0f:ac:39:b1: - f6:4b:8e:4e:f6:ea:e9:7b:e6:ba:5e:5a:8e:91:ef: - dc:b1:7d:52:3f:73:83:52:46:83:48:49:ff:f2:2d: - ca:54:f2:36:bb:49:cc:59:99:c0:9e:cf:8e:78:55: - 6c:ed:7d:7e:83:b8:59:2c:7d:f8:1a:81:f0:7d:f5: - 27:f2:db:ae:d4:31:54:38:fe:47:b2:ee:16:20:0f: - f1:db:2d:28:bf:6f:38:eb:11:bb:9a:d4:b2:5a:3a: - 4a:7f - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Alternative Name: - DNS:helm.sh, IP Address:127.0.0.1 - Signature Algorithm: sha256WithRSAEncryption - 4e:17:27:3d:36:4e:6c:2b:f7:d4:28:33:7e:05:26:7a:42:a0: - 2c:44:57:04:a0:de:df:40:fb:af:70:27:e6:55:20:f1:f8:c0: - 50:63:ab:b8:f1:31:5d:1e:f4:ca:8d:65:0b:d4:5e:5b:77:2f: - 2a:af:74:5f:18:2d:92:29:7f:2d:97:fb:ec:aa:e3:1e:db:b3: - 8d:01:aa:82:1a:f6:28:a8:b3:ee:15:9f:9a:f5:76:37:30:f2: - 3b:38:13:b2:d4:14:94:c6:38:fa:f9:6e:94:e8:1f:11:0b:b0: - 69:1a:b3:f9:f1:27:b4:d2:f5:64:54:7c:8f:e7:83:31:f6:0d: - a7:0e:0e:66:d8:33:2f:e0:a1:93:56:92:58:bf:50:da:56:8e: - db:42:22:f5:0c:6f:f8:4c:ef:f5:7c:2d:a6:b8:60:e4:bb:df: - a3:6c:c2:6b:99:0b:d3:0a:ad:7c:f4:74:72:9a:52:5e:81:d9: - a2:a2:dd:68:38:fb:b7:54:7f:f6:aa:ee:53:de:3d:3a:0e:86: - 53:ad:af:72:db:fb:6b:18:ce:ac:e4:64:70:13:68:da:be:e1: - 6b:46:dd:a0:72:96:9b:3f:ba:cf:11:6e:98:03:0a:69:83:9e: - 37:25:c9:36:b9:68:4f:73:ca:c6:32:5c:be:46:64:bb:a8:cc: - 71:25:8f:be ------BEGIN CERTIFICATE----- -MIIDRDCCAiygAwIBAgIUVTFTm0FyBdyQSb1IE3xZnlpTXoYwDQYJKoZIhvcNAQEL -BQAwTTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNPMRAwDgYDVQQHDAdCb3VsZGVy -MQ0wCwYDVQQKDARIZWxtMRAwDgYDVQQDDAdoZWxtLnNoMB4XDTE5MTEwMTIyNTE0 -OVoXDTI5MTAyOTIyNTE0OVowTTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNPMRAw -DgYDVQQHDAdCb3VsZGVyMQ0wCwYDVQQKDARIZWxtMRAwDgYDVQQDDAdoZWxtLnNo -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyIlVDQvx2ubAcH3TJ824 -qIGLfKSJ5dGxeAEd30SIC/zWgTU90Ttej7uTs34o2+3/oBM6cKP+lGsL/vtjALDL -3IHNgNzQL7+yT5qB1CLcl8iPJ4ZZkfqSBXXEzGv1qWt0HvXbqfi/jKIl/aDMefQl -V3SpI5vityJ6FHo96vF+MmtXbC7GT3VU+WtU0srrVByvORWb0HwP+FVRBOra+nuL -Yw+sObH2S45O9urpe+a6XlqOke/csX1SP3ODUkaDSEn/8i3KVPI2u0nMWZnAns+O -eFVs7X1+g7hZLH34GoHwffUn8tuu1DFUOP5Hsu4WIA/x2y0ov2846xG7mtSyWjpK -fwIDAQABoxwwGjAYBgNVHREEETAPggdoZWxtLnNohwR/AAABMA0GCSqGSIb3DQEB -CwUAA4IBAQBOFyc9Nk5sK/fUKDN+BSZ6QqAsRFcEoN7fQPuvcCfmVSDx+MBQY6u4 -8TFdHvTKjWUL1F5bdy8qr3RfGC2SKX8tl/vsquMe27ONAaqCGvYoqLPuFZ+a9XY3 -MPI7OBOy1BSUxjj6+W6U6B8RC7BpGrP58Se00vVkVHyP54Mx9g2nDg5m2DMv4KGT -VpJYv1DaVo7bQiL1DG/4TO/1fC2muGDku9+jbMJrmQvTCq189HRymlJegdmiot1o -OPu3VH/2qu5T3j06DoZTra9y2/trGM6s5GRwE2javuFrRt2gcpabP7rPEW6YAwpp -g543Jck2uWhPc8rGMly+RmS7qMxxJY++ ------END CERTIFICATE----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/key.pem b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/key.pem deleted file mode 100644 index 691e55087e3..00000000000 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpgIBAAKCAQEAyIlVDQvx2ubAcH3TJ824qIGLfKSJ5dGxeAEd30SIC/zWgTU9 -0Ttej7uTs34o2+3/oBM6cKP+lGsL/vtjALDL3IHNgNzQL7+yT5qB1CLcl8iPJ4ZZ -kfqSBXXEzGv1qWt0HvXbqfi/jKIl/aDMefQlV3SpI5vityJ6FHo96vF+MmtXbC7G -T3VU+WtU0srrVByvORWb0HwP+FVRBOra+nuLYw+sObH2S45O9urpe+a6XlqOke/c -sX1SP3ODUkaDSEn/8i3KVPI2u0nMWZnAns+OeFVs7X1+g7hZLH34GoHwffUn8tuu -1DFUOP5Hsu4WIA/x2y0ov2846xG7mtSyWjpKfwIDAQABAoIBAQC/XB1m58EQzCVS -sx7t2qedVJEQjcpxHdql0xr4VOMl3U2r2mx03pxrt+lH3NmMlN3bmL2pgzSJ2GSI -Gsbsf8jpUIwTraKUDe9PevbswZ+Sz3Wbl96dKGhzAWCcWWEBHGKgsKe+2Hmg75Il -Jm446btAaziDnFuJukKYi9XN/kgYPxi914O8yz2KtCIVHEHHkl1FcSqjpghPtzU3 -hm1Nv/7tW2r5IrxCGRNJQTg6l4A4mdqif1u75ZUMcbp8dTaJ2/iYBIKIsh7sFMqy -TG6ZN0p3G92ijo7rtznxXS9rIE2rcg6qhusdK8eqhV0KHOqH2nkB4jWbw1NwKFzV -2jXm4S5RAoGBAPIExNBpE30c++Wl4ITuzODd99CczFj527ZBxUdT/H/IszR7adtJ -gHnayzzycul3GnCVMEGBUBp7q09OkcacA7MqS3/Zjn2zrpViz2iluP6jl0qfs2Sp -HaePLBKz9oFVi5m17ZYYnG7etSPVzcLaEi23ws5286HToXeqfUuGd+DlAoGBANQf -FJzQ0EbNu5QcNnQqwfAahvSqc+imPL0HuQWKEMvN3UXXU7Nn8bqba/JGVhgD7/5u -3g2DyyIou6gnocN669CqY8hm0jEboggD4pC8LVj+Iot25UzoNeNuHfqeu7wAlWWL -zjfC3UpSbh1O4H8i5chpFxe9N7syzOXBI5IVPBuTAoGBAITrrZSxQSzj8E0uj2Mz -LH8MKgD/PRRZFhzBfrIwJGuiNRpL9dWkRtWmHx14IziqW3Ed3wT7Gp2Q8oN6KYIl -SbrrLdAoEqRjPS16uWNGMZZZDszDbWmJoGnYrmIPSQG7lBJ14uke1zvlQSNPV9T+ -pCFL3cg7eI+WhgYNMwd58PkpAoGBAKTXFlyaxRAQtrFtjz+NLrMY2kFt6K8l6FN5 -meXdGhpW+5pXsBreLvK17xgSYrs87BbML1FPVt9Pyiztx36ymmjI0MweYz94Wt1h -r4KMSa07qLq6hYzTc3Uu0Ks/CWMbDP4hu/qHOxKTpjCuaDVEeE7ao/B1wcZ+vs3Y -3nyadeBzAoGBAJAZl50nHPwXpEIsHO3nC1ff51cVoV3+gpcCgQ270rLEa2Uv8+Zc -8rXD/LgcLzZ6Fvp0I3jv1mXlN8W0OruZS71lCM/zBd++E04HMxcvuv4lfqzcW+3E -V0ZBn2ErSTF9yKvGedRJk+vbCi7cy38WaA+z59ct/gpiw2Z3q6w85jlF ------END RSA PRIVATE KEY----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/kind-cluster-setup.sh b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/kind-cluster-setup.sh index fd43ca12a0b..7a0e3e4fb4a 100755 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/kind-cluster-setup.sh +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/kind-cluster-setup.sh @@ -10,7 +10,7 @@ set -o errexit set -o nounset set -o pipefail -TAG=0.0.9 +TAG=0.0.10 function deploy { docker build -t kubeapps/fluxv2plugin-testdata:$TAG . @@ -19,29 +19,40 @@ function deploy { kubectl create deployment --image=kubeapps/fluxv2plugin-testdata:$TAG fluxv2plugin-testdata-app --context kind-kubeapps kubectl set env deployment/fluxv2plugin-testdata-app DOMAIN=cluster --context kind-kubeapps kubectl expose deployment fluxv2plugin-testdata-app --port=80 --target-port=80 --name=fluxv2plugin-testdata-svc --context kind-kubeapps + kubectl expose deployment fluxv2plugin-testdata-app --port=443 --target-port=443 --name=fluxv2plugin-testdata-ssl-svc --context kind-kubeapps } function undeploy { kubectl delete svc/fluxv2plugin-testdata-svc + kubectl delete svc/fluxv2plugin-testdata-ssl-svc kubectl delete deployment fluxv2plugin-testdata-app --context kind-kubeapps } +function redeploy { + undeploy + deploy +} + + function portforward { - kubectl -n default port-forward svc/fluxv2plugin-testdata-svc 8081:80 --context kind-kubeapps + #kubectl -n default port-forward svc/fluxv2plugin-testdata-svc 8081:80 --context kind-kubeapps + kubectl -n default port-forward svc/fluxv2plugin-testdata-ssl-svc 8081:443 --context kind-kubeapps } function shell { - kubectl exec --stdin --tty fluxv2plugin-testdata-app-74766cf559-695qg -- /bin/bash --context kind-kubeapps + #kubectl exec --stdin --tty fluxv2plugin-testdata-app-74766cf559-695qg -- /bin/bash --context kind-kubeapps + RANDOM=$$ + kubectl run -i --rm --tty centos-$RANDOM --image=centos --restart=Never -- /bin/bash } function logs { - kubectl logs pod/$(kubectl get pod -n default | grep fluxv2plugin | awk '{print $1}') -n default --context kind-kubeapps + kubectl logs pod/$(kubectl get pod -n default | grep fluxv2plugin | head -n 1 | awk '{print $1}') -n default --context kind-kubeapps } if [ $# -lt 1 ] then - echo "Usage : $0 deploy|undeploy|portforward|shell|logs" + echo "Usage : $0 deploy|undeploy|redeploy|portforward|shell|logs" exit fi @@ -50,6 +61,8 @@ deploy) deploy ;; undeploy) undeploy ;; +redeploy) redeploy + ;; portforward) portforward ;; shell) shell diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/nginx.conf b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/nginx.conf index 4045b481d3d..3ad7800ebff 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/nginx.conf +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/nginx.conf @@ -5,12 +5,24 @@ events { } http { server { - listen 80; + listen 80 default_server; root /usr/share/nginx/html; location /podinfo-basic-auth/ { auth_basic "Area secured with Basic Authentication"; auth_basic_user_file /etc/apache2/.htpasswd; } + + # contents of /podinfo-tls can only be accessed over SSL + location /podinfo-tls/ { + deny all; + } + } + + server { + listen 443 ssl; + root /usr/share/nginx/html/podinfo-tls/; + ssl_certificate /etc/ssl/certs/ssl-bundle.pem; + ssl_certificate_key /etc/ssl/certs/server-key.pem; } -} +} diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-tls-index.yaml b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-tls-index.yaml new file mode 100644 index 00000000000..74145228e21 --- /dev/null +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/podinfo-tls-index.yaml @@ -0,0 +1,41 @@ +# Copyright 2021-2022 the Kubeapps contributors. +# SPDX-License-Identifier: Apache-2.0 + +# This is local copy of the first few entries +# on "https://stefanprodan.github.io/podinfo/index.yaml" as of Sept 10 2021 with the chart +# urls modified to link to .tgz files also within the local cluster +apiVersion: v1 +entries: + podinfo: + - apiVersion: v1 + appVersion: 6.0.0 + created: "2021-06-16T12:51:07.137321429Z" + description: Podinfo Helm chart for Kubernetes + digest: aed86105891a9b1db588128878b79425cf6447e69b1012119e82c1d37c68ae67 + home: https://github.com/stefanprodan/podinfo + kubeVersion: ">=1.19.0-0" + maintainers: + - email: stefanprodan@users.noreply.github.com + name: stefanprodan + name: podinfo + sources: + - https://github.com/stefanprodan/podinfo + urls: + - https://fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local:443/podinfo-6.0.0.tgz + version: 6.0.0 + - apiVersion: v1 + appVersion: 5.2.1 + created: "2021-05-13T12:57:54.003168505Z" + description: Podinfo Helm chart for Kubernetes + digest: 6c3cc3b955bce1686036ae6822ee2ca0ef6ecb994e3f2d19eaf3ec03dcba84b3 + home: https://github.com/stefanprodan/podinfo + maintainers: + - email: stefanprodan@users.noreply.github.com + name: stefanprodan + name: podinfo + sources: + - https://github.com/stefanprodan/podinfo + urls: + - https://fluxv2plugin-testdata-ssl-svc.default.svc.cluster.local:443/podinfo-5.2.1.tgz + version: 5.2.1 +generated: "2021-06-16T12:51:07.135685319Z" diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-many-versions.yaml b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-many-versions.yaml index 269989d64fa..07fb1314e10 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-many-versions.yaml +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-many-versions.yaml @@ -37,7 +37,7 @@ entries: - http://redis.io/ urls: #- https://charts.bitnami.com/bitnami/redis-14.4.0.tgz - - "{{testdata/redis-14.4.0.tgz}}" + - "{{testdata/charts/redis-14.4.0.tgz}}" version: 14.4.0 - annotations: category: Database diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-two-versions.yaml b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-two-versions.yaml index 88d6d7fd60a..5c9828864e1 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-two-versions.yaml +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/redis-two-versions.yaml @@ -37,7 +37,7 @@ entries: - http://redis.io/ urls: #- https://charts.bitnami.com/bitnami/redis-14.4.0.tgz - - "{{testdata/redis-14.4.0.tgz}}" + - "{{testdata/charts/redis-14.4.0.tgz}}" version: 14.4.0 - annotations: category: Database @@ -72,5 +72,5 @@ entries: - http://redis.io/ urls: #- https://charts.bitnami.com/bitnami/redis-14.3.4.tgz - - "{{testdata/redis-14.3.4.tgz}}" + - "{{testdata/charts/redis-14.3.4.tgz}}" version: 14.3.4 diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.crt b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.crt deleted file mode 100644 index 892104365f4..00000000000 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCFAasUT/De3J4aee7b1VEESf+3ndyMA0GCSqGSIb3DQEBCwUAME0x -CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEQMA4GA1UEBwwHQm91bGRlcjENMAsG -A1UECgwESGVsbTEQMA4GA1UEAwwHaGVsbS5zaDAeFw0xOTExMDEyMjM2MzZaFw0y -MjA4MjEyMjM2MzZaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEQMA4GA1UE -BwwHQm91bGRlcjENMAsGA1UECgwESGVsbTEQMA4GA1UEAwwHaGVsbS5zaDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMinBcDJwiG3OVb1bCWQqTAOS3s6 -QwWkEXkoYyFFpCNvqEzQPtp+OkfD6gczc0ByGQibDLBApEQhq17inqtAxIUrTgXP -ym3l+0/U7ejuTka3ue84slkw2lVobfVEvJWGro+93GzbxvVNNYGJcD2BKJqmCCxD -I6tdTEL855kzgQUAvGITzDUxABU9+f06CW/9AlZlmBIuwrzRVjFNjflBrcm1PIUG -upMCu8zaWat8o1TnLCDKizw1JJzCgCnMxGXfzeAd1MGUG/rOFkBImHf39Jakp/7L -Icq+2FDE+0vNai0lpUpxPVTp8dcug8U3//bL3q0OqROA7Ks4wc0URGH71W8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAMJqzeg6cBbUkrh9a6+qa66IFR1Mf3wVB1c61 -JN6Z70kjgSdOZ/NexxxSu347fIPyKGkmokbnE1MJVEETPmzhpuTkQDcq7KT4IcQF -S+H4l0lNn09thIlIiAJmpQrNOlrHVtpLCFB4+YnsqqFKPlcO/dGy9U26L4xfn6+n -24/o7pNEu44GnktXPjfcbajaPUSKHxeYibjdftoUEYX/79ROu7E1QnNXj7mXymw0 -rqOgIlyCUGw8WvRR8RzR6m+1lnwOc+nxFKXzTt0LqOQt9sHI1V71WrxgDE+Lck+W -fybfsgodM2Y7VXnH4A4xoKeOHxW1YcqIKt0ribt8602lD1pYBg== ------END CERTIFICATE----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.key b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.key deleted file mode 100644 index e3c1ce51e6e..00000000000 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/rootCA.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAyKcFwMnCIbc5VvVsJZCpMA5LezpDBaQReShjIUWkI2+oTNA+ -2n46R8PqBzNzQHIZCJsMsECkRCGrXuKeq0DEhStOBc/KbeX7T9Tt6O5ORre57ziy -WTDaVWht9US8lYauj73cbNvG9U01gYlwPYEomqYILEMjq11MQvznmTOBBQC8YhPM -NTEAFT35/ToJb/0CVmWYEi7CvNFWMU2N+UGtybU8hQa6kwK7zNpZq3yjVOcsIMqL -PDUknMKAKczEZd/N4B3UwZQb+s4WQEiYd/f0lqSn/sshyr7YUMT7S81qLSWlSnE9 -VOnx1y6DxTf/9sverQ6pE4DsqzjBzRREYfvVbwIDAQABAoIBAHwyTbBP8baWx4oY -rNDvoplZL8VdgaCbNimNIxa0GW3Jrh2lhFIPcZl8HX5JjVvlg7M87XSm/kYhpQY9 -NUMA+uMGs+uK+1xcztpSDNRxtMe27wKwUEw+ndXhprX6ztOqop/cP/StcI/jM2wz -muKm8HAQttxWzlxCinKoQd4k8AYcnqc728FSODP7EsdDgiU6BhBZDqjgmqggye0y -niog+JBPDgwTgGodJWtSYuP/G2iJDUvm7bGU2gftXTJstrATLftGKX8XOgJMmDx9 -8OgDtU21LzggarOQ/iwUKX2MEfYnP8kgGLgu5nNonJCHWYGeCZoxIn70rs3WoBsU -5+FzmHkCgYEA7MFYixlTSxXfen1MwctuZ9YiwoneSLfjmBb+LP0Pfa2r0CVMPaXM -OexroIY14h64nunb7y3YifGk01RXzCBpEF5KhsZuYXAl3lGxbjbTjncU5/11Dim+ -W9g+T4zDimlK2tuweAjMfWz6XG2inZ3xvK73mGkEsUnqhWQKXBRf7VsCgYEA2PZp -KAwbpRFSYFwcZoRm81fLijZ5NbmOJtND6oG1LZVaVSYuvljvjQzeVfL4+Iju6FzT -zbnEfVsatu0cTs6jMy0yJUl6wRbHlH/G6Ra8UxSvUUEFe1Xap33RmjkK+atzALQi -pZPCIfLr+f9qQWrPMdZwzRnws0u2pKepSdXR0H0CgYB9chDdWyTkIwnPmDakdIri -X/b5Bx4Nf8oLGxvAcLHVkMD5v9l+zKvCgT+hxZslXcvK//S17Z/Pr4b7JrSChyXE -M4HfmaKA5HBcNQMDd+9ujDA6n/R29a1UcubJNbeiThoIjuEZKOhZCPY7JShFxZuB -s1+jlPmUiqrF1PUcRvtxAwKBgQDGpuelmWB+hRutyujeHQC+cnaU+EeHH3y+o9Wd -lGG1ePia2jkWZAwCU/QHMk8wEQDelJAB38O/G3mcYAH5Tk4zf4BYj6zrutXGbDBO -H1kToO7dMPG5+eQYU6Vk1jHsZEUKMeU/QckQmIHkBy7c8tT/Rt9FjCjNodd7b2Ab -kMFpaQKBgQDggmgsPFSZmo+yYDZucueXqfc8cbSWd9K1UruKMaPOsyoUWJNYARHA -cpHTpaIjDth8MUp2zLIZnPUSDkSgEAOcRH4C5CxmgSkmeJdlEEzWMF2yugczlYGO -l9SOX07w4/WJCZFeRWTqRGWs7X6iL8um0P9yFelw3SZt33ON+1fRPg== ------END RSA PRIVATE KEY----- diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/single-package-template.yaml b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/single-package-template.yaml index 0b6d5cc43aa..37c0736110d 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/single-package-template.yaml +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/single-package-template.yaml @@ -28,5 +28,5 @@ - https://github.com/bitnami/bitnami-docker-redis - http://redis.io/ urls: - - "{{testdata/redis-14.4.0.tgz}}" + - "{{testdata/charts/redis-14.4.0.tgz}}" version: 14.4.0 diff --git a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/valid-index.yaml b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/valid-index.yaml index 34da1c9562f..f0ecba48a64 100644 --- a/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/valid-index.yaml +++ b/cmd/kubeapps-apis/plugins/fluxv2/packages/v1alpha1/testdata/valid-index.yaml @@ -22,7 +22,7 @@ entries: - https://github.com/wbuchwalter/Kubernetes-acs-engine-autoscaler urls: #- https://kubernetes-charts.storage.googleapis.com/acs-engine-autoscaler-2.1.1.tgz - - "{{testdata/acs-engine-autoscaler-2.1.1.tgz}}" + - "{{testdata/charts/acs-engine-autoscaler-2.1.1.tgz}}" version: 2.1.1 wordpress: - appVersion: 4.9.1 @@ -48,7 +48,7 @@ entries: - https://github.com/bitnami/bitnami-docker-wordpress urls: #- https://kubernetes-charts.storage.googleapis.com/wordpress-0.7.5.tgz - - "{{testdata/wordpress-0.7.5.tgz}}" + - "{{testdata/charts/wordpress-0.7.5.tgz}}" version: 0.7.5 - appVersion: 4.9.0 created: 2017-12-01T11:49:00.136950565Z @@ -74,5 +74,5 @@ entries: - https://github.com/bitnami/bitnami-docker-wordpress urls: # - https://kubernetes-charts.storage.googleapis.com/wordpress-0.7.4.tgz - - "{{testdata/wordpress-0.7.4.tgz}}" + - "{{testdata/charts/wordpress-0.7.4.tgz}}" version: 0.7.4 diff --git a/cmd/kubeapps-apis/proto/kubeappsapis/core/packages/v1alpha1/repositories.proto b/cmd/kubeapps-apis/proto/kubeappsapis/core/packages/v1alpha1/repositories.proto index 1f4b67d91ea..16c812e48af 100644 --- a/cmd/kubeapps-apis/proto/kubeappsapis/core/packages/v1alpha1/repositories.proto +++ b/cmd/kubeapps-apis/proto/kubeappsapis/core/packages/v1alpha1/repositories.proto @@ -204,10 +204,10 @@ message SecretKeyReference { // that refers to it (e.g. PackageRepository), containing authentication // credentials for the said package repository. // - For HTTP/S basic auth the secret must be of type - // "kubernetes.io/basic-auth" and contain username and - // password fields. - // - For TLS the secret must be of type "kubernetes.io/tls" - // contain a certFile and keyFile, and/or + // "kubernetes.io/basic-auth" or opaque and contain username and + // password fields + // - For TLS the secret must be of type "kubernetes.io/tls" or opaque + // and contain a certFile and keyFile, and/or // caCert fields. // - For Bearer or Custom Auth, the secret must be opaque, and // the key must be provided diff --git a/script/makefiles/deploy-dev.mk b/script/makefiles/deploy-dev.mk index bed51ba8d63..8c89e3cc1fb 100644 --- a/script/makefiles/deploy-dev.mk +++ b/script/makefiles/deploy-dev.mk @@ -61,7 +61,7 @@ deploy-kapp-controller: # Add the flux controllers used for testing the kubeapps-apis integration. deploy-flux-controllers: - kubectl --kubeconfig=${CLUSTER_CONFIG} apply -f https://github.com/fluxcd/flux2/releases/download/v0.27.2/install.yaml + kubectl --kubeconfig=${CLUSTER_CONFIG} apply -f https://github.com/fluxcd/flux2/releases/download/v0.27.3/install.yaml reset-dev: helm --kubeconfig=${CLUSTER_CONFIG} -n kubeapps delete kubeapps || true